update from sparkleup
This commit is contained in:
parent
3b114127be
commit
6defee35c3
|
@ -104,7 +104,7 @@
|
|||
<p><img alt="Browser rendering of access.html page" src="https://assets.digitalocean.com/articles/eng_javascript/dom/html-rendering.png" /></p>
|
||||
<p>We’ll be using the different methods that we outlined in the <a href="https://www.digitalocean.com/community/tutorials/how-to-access-elements-in-the-dom#overview">Overview</a> above to access the available elements in the file.</p>
|
||||
<h2 id="accessing-elements-by-id">Accessing Elements by ID</h2>
|
||||
<p>The easiest way to access a single element in the DOM is by its unique <a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/id">ID</a>. We can grab an element by ID with the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementById"><code>getElementById()</code></a> method of the <code>document</code> object.</p>
|
||||
<p>The easiest way to access a single element in the DOM is by its unique <a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/id">ID</a>. You can get an element by ID with the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementById"><code>getElementById()</code></a> method of the <code>document</code> object.</p>
|
||||
<div class="codehilite"><pre><span></span><code><span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">();</span>
|
||||
</code></pre></div>
|
||||
|
||||
|
@ -125,10 +125,10 @@ const demoId = document.getElementById(‘demo’);</p>
|
|||
<span class="na"><div id</span><span class="o">=</span><span class="s">"demo">Access me by ID</div></span><span class="w"></span>
|
||||
</code></pre></div>
|
||||
|
||||
<p>We can be sure we’re accessing the correct element by changing the <code>border</code> property to <code>purple</code>.</p>
|
||||
<p>You can be sure you’re accessing the correct element by changing the <code>border</code> property to <code>purple</code>.</p>
|
||||
<p>```custom_prefix(>)
|
||||
demoId.style.border = ‘1px solid purple’;</p>
|
||||
<div class="codehilite"><pre><span></span><code><span class="n">Once</span><span class="w"> </span><span class="n">we</span><span class="w"> </span><span class="n">do</span><span class="w"> </span><span class="n">so</span><span class="p">,</span><span class="w"> </span><span class="n">our</span><span class="w"> </span><span class="n">live</span><span class="w"> </span><span class="n">page</span><span class="w"> </span><span class="n">will</span><span class="w"> </span><span class="n">look</span><span class="w"> </span><span class="ow">like</span><span class="w"> </span><span class="nl">this</span><span class="p">:</span><span class="w"></span>
|
||||
<div class="codehilite"><pre><span></span><code><span class="n">Once</span><span class="w"> </span><span class="n">you</span><span class="w"> </span><span class="n">do</span><span class="w"> </span><span class="n">so</span><span class="p">,</span><span class="w"> </span><span class="n">your</span><span class="w"> </span><span class="n">live</span><span class="w"> </span><span class="n">page</span><span class="w"> </span><span class="n">will</span><span class="w"> </span><span class="n">look</span><span class="w"> </span><span class="ow">like</span><span class="w"> </span><span class="nl">this</span><span class="p">:</span><span class="w"></span>
|
||||
|
||||
<span class="err">!</span><span class="o">[</span><span class="n">Browser rendering of ID element styling</span><span class="o">]</span><span class="p">(</span><span class="nl">https</span><span class="p">:</span><span class="o">//</span><span class="n">assets</span><span class="p">.</span><span class="n">digitalocean</span><span class="p">.</span><span class="n">com</span><span class="o">/</span><span class="n">articles</span><span class="o">/</span><span class="n">eng_javascript</span><span class="o">/</span><span class="n">dom</span><span class="o">/</span><span class="n">id</span><span class="o">-</span><span class="k">element</span><span class="p">.</span><span class="n">png</span><span class="p">)</span><span class="w"></span>
|
||||
|
||||
|
@ -136,7 +136,7 @@ demoId.style.border = ‘1px solid purple’;</p>
|
|||
|
||||
<span class="err">##</span><span class="w"> </span><span class="n">Accessing</span><span class="w"> </span><span class="n">Elements</span><span class="w"> </span><span class="k">by</span><span class="w"> </span><span class="k">Class</span><span class="w"></span>
|
||||
|
||||
<span class="n">The</span><span class="w"> </span><span class="o">[</span><span class="n">class</span><span class="o">]</span><span class="p">(</span><span class="nl">https</span><span class="p">:</span><span class="o">//</span><span class="n">developer</span><span class="p">.</span><span class="n">mozilla</span><span class="p">.</span><span class="n">org</span><span class="o">/</span><span class="n">en</span><span class="o">-</span><span class="n">US</span><span class="o">/</span><span class="n">docs</span><span class="o">/</span><span class="n">Web</span><span class="o">/</span><span class="n">HTML</span><span class="o">/</span><span class="n">Global_attributes</span><span class="o">/</span><span class="k">class</span><span class="p">)</span><span class="w"> </span><span class="n">attribute</span><span class="w"> </span><span class="k">is</span><span class="w"> </span><span class="n">used</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="n">access</span><span class="w"> </span><span class="n">one</span><span class="w"> </span><span class="ow">or</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">specific</span><span class="w"> </span><span class="n">elements</span><span class="w"> </span><span class="ow">in</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">DOM</span><span class="p">.</span><span class="w"> </span><span class="n">We</span><span class="w"> </span><span class="n">can</span><span class="w"> </span><span class="k">get</span><span class="w"> </span><span class="ow">all</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">elements</span><span class="w"> </span><span class="k">with</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">given</span><span class="w"> </span><span class="k">class</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="k">with</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="o">[</span><span class="n">`getElementsByClassName()`</span><span class="o">]</span><span class="p">(</span><span class="nl">https</span><span class="p">:</span><span class="o">//</span><span class="n">developer</span><span class="p">.</span><span class="n">mozilla</span><span class="p">.</span><span class="n">org</span><span class="o">/</span><span class="n">en</span><span class="o">-</span><span class="n">US</span><span class="o">/</span><span class="n">docs</span><span class="o">/</span><span class="n">Web</span><span class="o">/</span><span class="n">API</span><span class="o">/</span><span class="n">Document</span><span class="o">/</span><span class="n">getElementsByClassName</span><span class="p">)</span><span class="w"> </span><span class="k">method</span><span class="p">.</span><span class="w"></span>
|
||||
<span class="n">The</span><span class="w"> </span><span class="o">[</span><span class="n">class</span><span class="o">]</span><span class="p">(</span><span class="nl">https</span><span class="p">:</span><span class="o">//</span><span class="n">developer</span><span class="p">.</span><span class="n">mozilla</span><span class="p">.</span><span class="n">org</span><span class="o">/</span><span class="n">en</span><span class="o">-</span><span class="n">US</span><span class="o">/</span><span class="n">docs</span><span class="o">/</span><span class="n">Web</span><span class="o">/</span><span class="n">HTML</span><span class="o">/</span><span class="n">Global_attributes</span><span class="o">/</span><span class="k">class</span><span class="p">)</span><span class="w"> </span><span class="n">attribute</span><span class="w"> </span><span class="k">is</span><span class="w"> </span><span class="n">used</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="n">access</span><span class="w"> </span><span class="n">one</span><span class="w"> </span><span class="ow">or</span><span class="w"> </span><span class="n">more</span><span class="w"> </span><span class="k">specific</span><span class="w"> </span><span class="n">elements</span><span class="w"> </span><span class="ow">in</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">DOM</span><span class="p">.</span><span class="w"> </span><span class="n">You</span><span class="w"> </span><span class="n">can</span><span class="w"> </span><span class="k">get</span><span class="w"> </span><span class="ow">all</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">elements</span><span class="w"> </span><span class="k">with</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">given</span><span class="w"> </span><span class="k">class</span><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="k">with</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="o">[</span><span class="n">`getElementsByClassName()`</span><span class="o">]</span><span class="p">(</span><span class="nl">https</span><span class="p">:</span><span class="o">//</span><span class="n">developer</span><span class="p">.</span><span class="n">mozilla</span><span class="p">.</span><span class="n">org</span><span class="o">/</span><span class="n">en</span><span class="o">-</span><span class="n">US</span><span class="o">/</span><span class="n">docs</span><span class="o">/</span><span class="n">Web</span><span class="o">/</span><span class="n">API</span><span class="o">/</span><span class="n">Document</span><span class="o">/</span><span class="n">getElementsByClassName</span><span class="p">)</span><span class="w"> </span><span class="k">method</span><span class="p">.</span><span class="w"></span>
|
||||
|
||||
<span class="err">```</span><span class="n">js</span><span class="w"></span>
|
||||
<span class="n">document</span><span class="p">.</span><span class="n">getElementsByClassName</span><span class="p">();</span><span class="w"></span>
|
||||
|
@ -147,10 +147,10 @@ demoId.style.border = ‘1px solid purple’;</p>
|
|||
<span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">"demo"</span><span class="p">></span>Access me by class (2)<span class="p"></</span><span class="nt">div</span><span class="p">></span>
|
||||
</code></pre></div>
|
||||
|
||||
<p>Let’s access our elements in the <em>Console</em> and put them in a variable called <code>demoClass</code>.</p>
|
||||
<p>Access these elements in the <em>Console</em> and put them in a variable called <code>demoClass</code>.</p>
|
||||
<p>```custom_prefix(>)
|
||||
const demoClass = document.getElementsByClassName(‘demo’);</p>
|
||||
<div class="codehilite"><pre><span></span><code><span class="k">At</span><span class="w"> </span><span class="n">this</span><span class="w"> </span><span class="kt">point</span><span class="p">,</span><span class="w"> </span><span class="n">you</span><span class="w"> </span><span class="n">might</span><span class="w"> </span><span class="n">think</span><span class="w"> </span><span class="n">you</span><span class="w"> </span><span class="n">can</span><span class="w"> </span><span class="k">modify</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">elements</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">same</span><span class="w"> </span><span class="n">way</span><span class="w"> </span><span class="n">you</span><span class="w"> </span><span class="n">did</span><span class="w"> </span><span class="k">with</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">ID</span><span class="w"> </span><span class="n">example</span><span class="p">.</span><span class="w"> </span><span class="n">However</span><span class="p">,</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">we</span><span class="w"> </span><span class="n">try</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="n">run</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="k">following</span><span class="w"> </span><span class="k">code</span><span class="w"> </span><span class="k">and</span><span class="w"> </span><span class="k">change</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n n-Quoted">`border`</span><span class="w"> </span><span class="n">property</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">class</span><span class="w"> </span><span class="n">demo</span><span class="w"> </span><span class="n">elements</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="n">orange</span><span class="p">,</span><span class="w"> </span><span class="n">we</span><span class="w"> </span><span class="n">will</span><span class="w"> </span><span class="k">get</span><span class="w"> </span><span class="n">an</span><span class="w"> </span><span class="k">error</span><span class="p">.</span><span class="w"></span>
|
||||
<div class="codehilite"><pre><span></span><code><span class="k">At</span><span class="w"> </span><span class="n">this</span><span class="w"> </span><span class="kt">point</span><span class="p">,</span><span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="n">might</span><span class="w"> </span><span class="n">be</span><span class="w"> </span><span class="n">tempting</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="k">modify</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">elements</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">same</span><span class="w"> </span><span class="n">way</span><span class="w"> </span><span class="n">you</span><span class="w"> </span><span class="n">did</span><span class="w"> </span><span class="k">with</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">ID</span><span class="w"> </span><span class="n">example</span><span class="p">.</span><span class="w"> </span><span class="n">However</span><span class="p">,</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">you</span><span class="w"> </span><span class="n">try</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="n">run</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="k">following</span><span class="w"> </span><span class="k">code</span><span class="w"> </span><span class="k">and</span><span class="w"> </span><span class="k">change</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n n-Quoted">`border`</span><span class="w"> </span><span class="n">property</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">class</span><span class="w"> </span><span class="n">demo</span><span class="w"> </span><span class="n">elements</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="n">orange</span><span class="p">,</span><span class="w"> </span><span class="n">you</span><span class="w"> </span><span class="n">will</span><span class="w"> </span><span class="k">get</span><span class="w"> </span><span class="n">an</span><span class="w"> </span><span class="k">error</span><span class="p">.</span><span class="w"></span>
|
||||
|
||||
<span class="n n-Quoted">`</span><span class="n n-Quoted n-Quoted-Escape">``</span><span class="n n-Quoted">custom_prefix(>)</span>
|
||||
<span class="n n-Quoted">demoClass.style.border = '1px solid orange';</span>
|
||||
|
@ -160,7 +160,7 @@ const demoClass = document.getElementsByClassName(‘demo’);</p>
|
|||
<span class="na">Uncaught TypeError: Cannot set property 'border' of undefined</span><span class="w"></span>
|
||||
</code></pre></div>
|
||||
|
||||
<p>The reason this doesn’t work is because instead of just getting one element, we have an array-like object of elements.</p>
|
||||
<p>The reason this doesn’t work is because instead of just getting one element, you have an array-like object of elements.</p>
|
||||
<p>```custom_prefix(>)
|
||||
console.log(demoClass);</p>
|
||||
<div class="codehilite"><pre><span></span><code>
|
||||
|
@ -168,26 +168,26 @@ console.log(demoClass);</p>
|
|||
|
||||
<p>[secondary_label Output]
|
||||
(2) [div.demo, div.demo]</p>
|
||||
<div class="codehilite"><pre><span></span><code><span class="p">[</span><span class="n">JavaScript</span><span class="w"> </span><span class="n">arrays</span><span class="p">](</span><span class="n">https</span><span class="o">:</span><span class="c1">//www.digitalocean.com/community/tutorials/understanding-arrays-in-javascript) must be accessed with an index number. We can therefore change the first element of this array by using an index of `0`.</span>
|
||||
<div class="codehilite"><pre><span></span><code><span class="p">[</span><span class="n">JavaScript</span><span class="w"> </span><span class="n">arrays</span><span class="p">](</span><span class="n">https</span><span class="o">:</span><span class="c1">//www.digitalocean.com/community/tutorials/understanding-arrays-in-javascript) must be accessed with an index number. You can change the first element of this array by using an index of `0`.</span>
|
||||
|
||||
<span class="err">```</span><span class="n">custom_prefix</span><span class="p">(</span><span class="o">></span><span class="p">)</span><span class="w"></span>
|
||||
<span class="n">demoClass</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">style</span><span class="p">.</span><span class="n">border</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="err">'</span><span class="mi">1</span><span class="n">px</span><span class="w"> </span><span class="n">solid</span><span class="w"> </span><span class="n">orange</span><span class="err">'</span><span class="p">;</span><span class="w"></span>
|
||||
</code></pre></div>
|
||||
|
||||
<p>Generally when accessing elements by class, we want to apply a change to all the elements in the document with that particular class, not just one. We can do this by creating a <code>for</code> loop, and looping through every item in the array. </p>
|
||||
<p>Generally when accessing elements by class, we want to apply a change to all the elements in the document with that particular class, not just one. You can do this by creating a <code>for</code> loop, and looping through every item in the array. </p>
|
||||
<p>```custom_prefix(>)
|
||||
for (i = 0; i < demoClass.length; i++) {
|
||||
demoClass[i].style.border = ‘1px solid orange’;
|
||||
}</p>
|
||||
<div class="codehilite"><pre><span></span><code><span class="k">When</span><span class="w"> </span><span class="n">we</span><span class="w"> </span><span class="n">run</span><span class="w"> </span><span class="n">this</span><span class="w"> </span><span class="k">code</span><span class="p">,</span><span class="w"> </span><span class="n">our</span><span class="w"> </span><span class="n">live</span><span class="w"> </span><span class="k">page</span><span class="w"> </span><span class="n">will</span><span class="w"> </span><span class="n">be</span><span class="w"> </span><span class="n">rendered</span><span class="w"> </span><span class="k">like</span><span class="w"> </span><span class="n">this</span><span class="o">:</span><span class="w"></span>
|
||||
<div class="codehilite"><pre><span></span><code><span class="k">When</span><span class="w"> </span><span class="n">you</span><span class="w"> </span><span class="n">run</span><span class="w"> </span><span class="n">this</span><span class="w"> </span><span class="k">code</span><span class="p">,</span><span class="w"> </span><span class="n">your</span><span class="w"> </span><span class="n">live</span><span class="w"> </span><span class="k">page</span><span class="w"> </span><span class="n">will</span><span class="w"> </span><span class="n">be</span><span class="w"> </span><span class="n">rendered</span><span class="w"> </span><span class="k">like</span><span class="w"> </span><span class="n">this</span><span class="o">:</span><span class="w"></span>
|
||||
|
||||
<span class="o">!</span><span class="err">[</span><span class="n">Browser</span><span class="w"> </span><span class="n">rendering</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="n">class</span><span class="w"> </span><span class="n">element</span><span class="w"> </span><span class="n">styling</span><span class="err">]</span><span class="p">(</span><span class="n">https</span><span class="o">://</span><span class="n">assets</span><span class="p">.</span><span class="n">digitalocean</span><span class="p">.</span><span class="n">com</span><span class="o">/</span><span class="n">articles</span><span class="o">/</span><span class="n">eng_javascript</span><span class="o">/</span><span class="n">dom</span><span class="o">/</span><span class="n">class</span><span class="o">-</span><span class="n">element</span><span class="p">.</span><span class="n">png</span><span class="p">)</span><span class="w"></span>
|
||||
|
||||
<span class="n">We</span><span class="w"> </span><span class="n">have</span><span class="w"> </span><span class="n">now</span><span class="w"> </span><span class="n">selected</span><span class="w"> </span><span class="k">every</span><span class="w"> </span><span class="n">element</span><span class="w"> </span><span class="k">on</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="k">page</span><span class="w"> </span><span class="n">that</span><span class="w"> </span><span class="n">has</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n n-Quoted">`demo`</span><span class="w"> </span><span class="n">class</span><span class="p">,</span><span class="w"> </span><span class="k">and</span><span class="w"> </span><span class="k">changed</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n n-Quoted">`border`</span><span class="w"> </span><span class="n">property</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="n n-Quoted">`orange`</span><span class="p">.</span><span class="w"></span>
|
||||
<span class="n">You</span><span class="w"> </span><span class="n">have</span><span class="w"> </span><span class="n">now</span><span class="w"> </span><span class="n">selected</span><span class="w"> </span><span class="k">every</span><span class="w"> </span><span class="n">element</span><span class="w"> </span><span class="k">on</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="k">page</span><span class="w"> </span><span class="n">that</span><span class="w"> </span><span class="n">has</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n n-Quoted">`demo`</span><span class="w"> </span><span class="n">class</span><span class="p">,</span><span class="w"> </span><span class="k">and</span><span class="w"> </span><span class="k">changed</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n n-Quoted">`border`</span><span class="w"> </span><span class="n">property</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="n n-Quoted">`orange`</span><span class="p">.</span><span class="w"></span>
|
||||
|
||||
<span class="c1">## Accessing Elements by Tag</span><span class="w"></span>
|
||||
|
||||
<span class="n">A</span><span class="w"> </span><span class="k">less</span><span class="w"> </span><span class="k">specific</span><span class="w"> </span><span class="n">way</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="n">access</span><span class="w"> </span><span class="n">multiple</span><span class="w"> </span><span class="n">elements</span><span class="w"> </span><span class="k">on</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="k">page</span><span class="w"> </span><span class="n">would</span><span class="w"> </span><span class="n">be</span><span class="w"> </span><span class="k">by</span><span class="w"> </span><span class="n">its</span><span class="w"> </span><span class="n">HTML</span><span class="w"> </span><span class="n">tag</span><span class="w"> </span><span class="k">name</span><span class="p">.</span><span class="w"> </span><span class="n">We</span><span class="w"> </span><span class="n">access</span><span class="w"> </span><span class="n">an</span><span class="w"> </span><span class="n">element</span><span class="w"> </span><span class="k">by</span><span class="w"> </span><span class="n">tag</span><span class="w"> </span><span class="k">with</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="err">[</span><span class="n n-Quoted">`getElementsByTagName()`</span><span class="err">]</span><span class="p">(</span><span class="n">https</span><span class="o">://</span><span class="n">developer</span><span class="p">.</span><span class="n">mozilla</span><span class="p">.</span><span class="n">org</span><span class="o">/</span><span class="n">en</span><span class="o">-</span><span class="n">US</span><span class="o">/</span><span class="n">docs</span><span class="o">/</span><span class="n">Web</span><span class="o">/</span><span class="n">API</span><span class="o">/</span><span class="n">Element</span><span class="o">/</span><span class="n">getElementsByTagName</span><span class="p">)</span><span class="w"> </span><span class="n">method</span><span class="p">.</span><span class="w"></span>
|
||||
<span class="n">A</span><span class="w"> </span><span class="k">less</span><span class="w"> </span><span class="k">specific</span><span class="w"> </span><span class="n">way</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="n">access</span><span class="w"> </span><span class="n">multiple</span><span class="w"> </span><span class="n">elements</span><span class="w"> </span><span class="k">on</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="k">page</span><span class="w"> </span><span class="n">would</span><span class="w"> </span><span class="n">be</span><span class="w"> </span><span class="k">by</span><span class="w"> </span><span class="n">its</span><span class="w"> </span><span class="n">HTML</span><span class="w"> </span><span class="n">tag</span><span class="w"> </span><span class="k">name</span><span class="p">.</span><span class="w"> </span><span class="n">You</span><span class="w"> </span><span class="n">access</span><span class="w"> </span><span class="n">an</span><span class="w"> </span><span class="n">element</span><span class="w"> </span><span class="k">by</span><span class="w"> </span><span class="n">tag</span><span class="w"> </span><span class="k">with</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="err">[</span><span class="n n-Quoted">`getElementsByTagName()`</span><span class="err">]</span><span class="p">(</span><span class="n">https</span><span class="o">://</span><span class="n">developer</span><span class="p">.</span><span class="n">mozilla</span><span class="p">.</span><span class="n">org</span><span class="o">/</span><span class="n">en</span><span class="o">-</span><span class="n">US</span><span class="o">/</span><span class="n">docs</span><span class="o">/</span><span class="n">Web</span><span class="o">/</span><span class="n">API</span><span class="o">/</span><span class="n">Element</span><span class="o">/</span><span class="n">getElementsByTagName</span><span class="p">)</span><span class="w"> </span><span class="n">method</span><span class="p">.</span><span class="w"></span>
|
||||
|
||||
<span class="n n-Quoted">`</span><span class="n n-Quoted n-Quoted-Escape">``</span><span class="n n-Quoted">js</span>
|
||||
<span class="n n-Quoted">document.getElementsByTagName();</span>
|
||||
|
@ -198,7 +198,7 @@ for (i = 0; i < demoClass.length; i++) {
|
|||
<span class="nt"><article></span>Access me by tag (2)<span class="nt"></article></span>
|
||||
</code></pre></div>
|
||||
|
||||
<p>Just like accessing an element by its class, <code>getElementsByTagName()</code> will return an array-like object of elements, and we can modify every tag in the document with a <code>for</code> loop.</p>
|
||||
<p>Just like accessing an element by its class, <code>getElementsByTagName()</code> will return an array-like object of elements, and you can modify every tag in the document with a <code>for</code> loop.</p>
|
||||
<p>```custom_prefix(>)
|
||||
const demoTag = document.getElementsByTagName(‘article’);</p>
|
||||
<p>for (i = 0; i < demoTag.length; i++) {
|
||||
|
@ -218,31 +218,31 @@ const demoTag = document.getElementsByTagName(‘article’);</p>
|
|||
<span class="s1">$('</span><span class="c1">#demo'); // returns the demo ID element in jQuery</span><span class="w"></span>
|
||||
</code></pre></div>
|
||||
|
||||
<p>We can do the same in plain JavaScript with the <code>querySelector()</code> and <code>querySelectorAll()</code> methods.</p>
|
||||
<p>You can do the same in plain JavaScript with the <code>querySelector()</code> and <code>querySelectorAll()</code> methods.</p>
|
||||
<div class="codehilite"><pre><span></span><code><span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">();</span>
|
||||
<span class="nb">document</span><span class="p">.</span><span class="nx">querySelectorAll</span><span class="p">();</span>
|
||||
</code></pre></div>
|
||||
|
||||
<p>To access a single element, we will use the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector"><code>querySelector()</code></a> method. In our HTML file, we have a <code>demo-query</code> element</p>
|
||||
<p>To access a single element, you can use the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector"><code>querySelector()</code></a> method. In our HTML file, we have a <code>demo-query</code> element</p>
|
||||
<div class="codehilite"><pre><span></span><code><span class="p"><</span><span class="nt">div</span> <span class="na">id</span><span class="o">=</span><span class="s">"demo-query"</span><span class="p">></span>Access me by query<span class="p"></</span><span class="nt">div</span><span class="p">></span>
|
||||
</code></pre></div>
|
||||
|
||||
<p>The selector for an <code>id</code> attribute is the hash symbol (<code>#</code>). We can assign the element with the <code>demo-query</code> id to the <code>demoQuery</code> variable.</p>
|
||||
<p>The selector for an <code>id</code> attribute is the hash symbol (<code>#</code>). You can assign the element with the <code>demo-query</code> id to the <code>demoQuery</code> variable.</p>
|
||||
<p>```custom_prefix(>)
|
||||
const demoQuery = document.querySelector(‘#demo-query’);</p>
|
||||
<div class="codehilite"><pre><span></span><code>In the case of a selector with multiple elements, such as a class or a tag, `querySelector()` will return the first element that matches the query. We can use the [`querySelectorAll()`](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll) method to collect all the elements that match a specific query.
|
||||
<div class="codehilite"><pre><span></span><code>In the case of a selector with multiple elements, such as a class or a tag, `querySelector()` will return the first element that matches the query. You can use the [`querySelectorAll()`](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll) method to collect all the elements that match a specific query.
|
||||
|
||||
In our example file, we have two elements with the `demo-query-all` class applied to them.
|
||||
In the example file, you have two elements with the `demo-query-all` class applied to them.
|
||||
|
||||
```html
|
||||
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"demo-query-all"</span><span class="nt">></span>Access me by query all (1)<span class="nt"></div></span>
|
||||
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"demo-query-all"</span><span class="nt">></span>Access me by query all (2)<span class="nt"></div></span>
|
||||
</code></pre></div>
|
||||
|
||||
<p>The selector for a <code>class</code> attribute is a period or full stop (<code>.</code>), so we can access the class with <code>.demo-query-all</code>.</p>
|
||||
<p>The selector for a <code>class</code> attribute is a period or full stop (<code>.</code>), so you can access the class with <code>.demo-query-all</code>.</p>
|
||||
<p>```custom_prefix(>)
|
||||
const demoQueryAll = document.querySelectorAll(‘.demo-query-all’);</p>
|
||||
<div class="codehilite"><pre><span></span><code><span class="k">Using</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n n-Quoted">`forEach()`</span><span class="w"> </span><span class="n">method</span><span class="p">,</span><span class="w"> </span><span class="n">we</span><span class="w"> </span><span class="n">can</span><span class="w"> </span><span class="n">apply</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">color</span><span class="w"> </span><span class="n n-Quoted">`green`</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n n-Quoted">`border`</span><span class="w"> </span><span class="n">property</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="k">all</span><span class="w"> </span><span class="n">matching</span><span class="w"> </span><span class="n">elements</span><span class="p">.</span><span class="w"></span>
|
||||
<div class="codehilite"><pre><span></span><code><span class="k">Using</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n n-Quoted">`forEach()`</span><span class="w"> </span><span class="n">method</span><span class="p">,</span><span class="w"> </span><span class="n">you</span><span class="w"> </span><span class="n">can</span><span class="w"> </span><span class="n">apply</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">color</span><span class="w"> </span><span class="n n-Quoted">`green`</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n n-Quoted">`border`</span><span class="w"> </span><span class="n">property</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="k">all</span><span class="w"> </span><span class="n">matching</span><span class="w"> </span><span class="n">elements</span><span class="p">.</span><span class="w"></span>
|
||||
|
||||
<span class="n n-Quoted">`</span><span class="n n-Quoted n-Quoted-Escape">``</span><span class="n n-Quoted">custom_prefix(>)</span>
|
||||
<span class="n n-Quoted">demoQueryAll.forEach(query => {</span>
|
||||
|
@ -254,7 +254,7 @@ const demoQueryAll = document.querySelectorAll(‘.demo-query-all’);</
|
|||
<p>With <code>querySelector()</code>, comma-separated values function as an OR operator. For example, <code>querySelector('div, article')</code> will match <code>div</code> <em>or</em> <code>article</code>, whichever appears first in the document. With <code>querySelectorAll()</code>, comma-separated values function as an AND operator, and <code>querySelectorAll('div, article')</code> will match all <code>div</code> <em>and</em> <code>article</code> values in the document.</p>
|
||||
<p>Using the query selector methods is extremely powerful, as you can access any element or group of elements in the DOM the same way you would in a CSS file. For a complete list of selectors, review <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Selectors">CSS Selectors</a> on the Mozilla Developer Network.</p>
|
||||
<h2 id="complete-javascript-code">Complete JavaScript Code</h2>
|
||||
<p>Below is the complete script of the work we did above. You can use it to access all the elements on our example page. Save the file as <code>access.js</code> and load it in to the HTML file right before the closing <code>body</code> tag.</p>
|
||||
<p>Below is the complete script of the work you did above. You can use it to access all the elements on our example page. Save the file as <code>access.js</code> and load it in to the HTML file right before the closing <code>body</code> tag.</p>
|
||||
<div class="codehilite"><pre><span></span><code><span class="p">[</span><span class="nx">label</span> <span class="nx">access</span><span class="p">.</span><span class="nx">js</span><span class="p">]</span>
|
||||
<span class="c1">// Assign all elements</span>
|
||||
<span class="kd">const</span> <span class="nx">demoId</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s1">'demo'</span><span class="p">);</span>
|
||||
|
@ -338,7 +338,7 @@ const demoQueryAll = document.querySelectorAll(‘.demo-query-all’);</
|
|||
<p>In this tutorial, we went over 5 ways to access HTML elements in the DOM — by ID, by class, by HTML tag name, and by selector. The method you will use to get an element or group of elements will depend on browser support and how many elements you will be manipulating. You should now feel confident to access any HTML element in a document with JavaScript through the DOM.</p>
|
||||
</article>
|
||||
<footer>
|
||||
<p>Page generated on 2022-06-22</p>
|
||||
<p>Page generated on 2022-06-28</p>
|
||||
</footer>
|
||||
</main>
|
||||
<script type="text/javascript">
|
||||
|
|
|
@ -0,0 +1,309 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Zk | ctm-514</title>
|
||||
<link rel="stylesheet" type="text/css" href="/style.css" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<meta charset="utf-8" />
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<header>
|
||||
<h1>Zk | ctm-514</h1>
|
||||
</header>
|
||||
<article class="content">
|
||||
<h3 id="introduction">Introduction</h3>
|
||||
<p><strong>Structures</strong>, or <strong>structs</strong>, are used to collect multiple pieces of information together in one unit. These <a href="https://www.digitalocean.com/community/tutorials/defining-structs-in-go">collections of information</a> are used to describe higher-level concepts, such as an <code>Address</code> composed of a <code>Street</code>, <code>City</code>, <code>State</code>, and <code>PostalCode</code>. When you read this information from systems such as databases, or APIs, you can use <strong>struct tags</strong> to control how this information is assigned to the fields of a struct. Struct tags are small pieces of metadata attached to fields of a struct that provide instructions to other Go code that works with the struct.</p>
|
||||
<h2 id="what-does-a-struct-tag-look-like">What Does a Struct Tag Look Like?</h2>
|
||||
<p>Go struct tags are annotations that appear after the type in a Go struct declaration. Each tag is composed of short strings associated with some corresponding value. </p>
|
||||
<p>A struct tag looks like this, with the tag offset with backtick <code>`</code> characters:</p>
|
||||
<div class="codehilite"><pre><span></span><code><span class="kd">type</span><span class="w"> </span><span class="nx">User</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">Name</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="s">`example:"name"`</span><span class="w"></span>
|
||||
<span class="p">}</span><span class="w"></span>
|
||||
</code></pre></div>
|
||||
|
||||
<p>Other Go code is then capable of examining these structs and extracting the values assigned to specific keys it requests. Struct tags have no effect on the operation of your code without additional code that examines them.</p>
|
||||
<p>Try this example to see what struct tags look like, and that without code from another package, they will have no effect.</p>
|
||||
<div class="codehilite"><pre><span></span><code><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w"></span>
|
||||
|
||||
<span class="kn">import</span><span class="w"> </span><span class="s">"fmt"</span><span class="w"></span>
|
||||
|
||||
<span class="kd">type</span><span class="w"> </span><span class="nx">User</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">Name</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="s">`example:"name"`</span><span class="w"></span>
|
||||
<span class="p">}</span><span class="w"></span>
|
||||
|
||||
<span class="kd">func</span><span class="w"> </span><span class="p">(</span><span class="nx">u</span><span class="w"> </span><span class="o">*</span><span class="nx">User</span><span class="p">)</span><span class="w"> </span><span class="nx">String</span><span class="p">()</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Sprintf</span><span class="p">(</span><span class="s">"Hi! My name is %s"</span><span class="p">,</span><span class="w"> </span><span class="nx">u</span><span class="p">.</span><span class="nx">Name</span><span class="p">)</span><span class="w"></span>
|
||||
<span class="p">}</span><span class="w"></span>
|
||||
|
||||
<span class="kd">func</span><span class="w"> </span><span class="nx">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">u</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="o">&</span><span class="nx">User</span><span class="p">{</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">Name</span><span class="p">:</span><span class="w"> </span><span class="s">"Sammy"</span><span class="p">,</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="p">}</span><span class="w"></span>
|
||||
|
||||
<span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">u</span><span class="p">)</span><span class="w"></span>
|
||||
<span class="p">}</span><span class="w"></span>
|
||||
</code></pre></div>
|
||||
|
||||
<p>This will output:</p>
|
||||
<div class="codehilite"><pre><span></span><code><span class="k">[secondary_label Output]</span><span class="w"></span>
|
||||
<span class="na">Hi! My name is Sammy</span><span class="w"></span>
|
||||
</code></pre></div>
|
||||
|
||||
<p>This example defines a <code>User</code> type with a <code>Name</code> field. The <code>Name</code> field has been given a struct tag of <code>example:"name"</code>. We would refer to this specific tag in conversation as the “example struct tag” because it uses the word “example” as its key. The <code>example</code> struct tag has the value <code>"name"</code> for the <code>Name</code> field. On the <code>User</code> type, we also define the <code>String()</code> method required by the <code>fmt.Stringer</code> interface. This will be called automatically when we pass the type to <code>fmt.Println</code> and gives us a chance to produce a nicely formatted version of our struct.</p>
|
||||
<p>Within the body of <code>main</code>, we create a new instance of our <code>User</code> type and pass it to <code>fmt.Println</code>. Even though the struct had a struct tag present, we see that it has no effect on the operation of this Go code. It will behave exactly the same if the struct tag were not present.</p>
|
||||
<p>To use struct tags to accomplish something, other Go code must be written to examine structs at runtime. The standard library has packages that use struct tags as part of their operation. The most popular of these is the <code>encoding/json</code> package.</p>
|
||||
<h2 id="encoding-json">Encoding JSON</h2>
|
||||
<p>JavaScript Object Notation (JSON) is a textual format for encoding collections of data organized under different string keys. It’s commonly used to communicate data between different programs as the format is simple enough that libraries exist to decode it in many different languages. The following is an example of JSON:</p>
|
||||
<div class="codehilite"><pre><span></span><code><span class="err">{</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="ss">"language"</span><span class="err">:</span><span class="w"> </span><span class="ss">"Go"</span><span class="p">,</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="ss">"mascot"</span><span class="err">:</span><span class="w"> </span><span class="ss">"Gopher"</span><span class="w"></span>
|
||||
<span class="err">}</span><span class="w"></span>
|
||||
</code></pre></div>
|
||||
|
||||
<p>This JSON object contains two keys, <code>language</code> and <code>mascot</code>. Following these keys are the associated values. Here the <code>language</code> key has a value of <code>Go</code> and <code>mascot</code> is assigned the value <code>Gopher</code>.</p>
|
||||
<p>The JSON encoder in the standard library makes use of struct tags as annotations indicating to the encoder how you would like to name your fields in the JSON output. These JSON encoding and decoding mechanisms can be found in the <code>encoding/json</code> <a href="https://pkg.go.dev/encoding/json">package</a>.</p>
|
||||
<p>Try this example to see how JSON is encoded without struct tags:</p>
|
||||
<div class="codehilite"><pre><span></span><code><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w"></span>
|
||||
|
||||
<span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="s">"encoding/json"</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="s">"fmt"</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="s">"log"</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="s">"os"</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="s">"time"</span><span class="w"></span>
|
||||
<span class="p">)</span><span class="w"></span>
|
||||
|
||||
<span class="kd">type</span><span class="w"> </span><span class="nx">User</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">Name</span><span class="w"> </span><span class="kt">string</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">Password</span><span class="w"> </span><span class="kt">string</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">PreferredFish</span><span class="w"> </span><span class="p">[]</span><span class="kt">string</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">CreatedAt</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Time</span><span class="w"></span>
|
||||
<span class="p">}</span><span class="w"></span>
|
||||
|
||||
<span class="kd">func</span><span class="w"> </span><span class="nx">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">u</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="o">&</span><span class="nx">User</span><span class="p">{</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">Name</span><span class="p">:</span><span class="w"> </span><span class="s">"Sammy the Shark"</span><span class="p">,</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">Password</span><span class="p">:</span><span class="w"> </span><span class="s">"fisharegreat"</span><span class="p">,</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">CreatedAt</span><span class="p">:</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Now</span><span class="p">(),</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="p">}</span><span class="w"></span>
|
||||
|
||||
<span class="w"> </span><span class="nx">out</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">json</span><span class="p">.</span><span class="nx">MarshalIndent</span><span class="p">(</span><span class="nx">u</span><span class="p">,</span><span class="w"> </span><span class="s">""</span><span class="p">,</span><span class="w"> </span><span class="s">" "</span><span class="p">)</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">log</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">os</span><span class="p">.</span><span class="nx">Exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="p">}</span><span class="w"></span>
|
||||
|
||||
<span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nb">string</span><span class="p">(</span><span class="nx">out</span><span class="p">))</span><span class="w"></span>
|
||||
<span class="p">}</span><span class="w"></span>
|
||||
</code></pre></div>
|
||||
|
||||
<p>This will print the following output:</p>
|
||||
<div class="codehilite"><pre><span></span><code><span class="k">[secondary_label Output]</span><span class="w"></span>
|
||||
<span class="na">{</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="na">"Name": "Sammy the Shark",</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="na">"Password": "fisharegreat",</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="na">"CreatedAt": "2019-09-23T15:50:01.203059-04:00"</span><span class="w"></span>
|
||||
<span class="na">}</span><span class="w"></span>
|
||||
</code></pre></div>
|
||||
|
||||
<p>We defined a struct describing a user with fields including their name, password, and the time the user was created. Within the <code>main</code> function, we create an instance of this user by supplying values for all fields except <code>PreferredFish</code> (Sammy likes all fish). We then passed the instance of <code>User</code> to the <code>json.MarshalIndent</code> function. This is used so we can more easily see the JSON output without using an external formatting tool. This call could be replaced with <code>json.Marshal(u)</code> to print JSON without any additional whitespace. The two additional arguments to <code>json.MarshalIndent</code> control the prefix to the output (which we have omitted with the empty string), and the characters to use for indenting, which here are two space characters. Any errors produced from <code>json.MarshalIndent</code> are logged and the program terminates using <code>os.Exit(1)</code>. Finally, we cast the <code>[]byte</code> returned from <code>json.MarshalIndent</code> to a <code>string</code> and passed the resulting string to <code>fmt.Println</code> for printing on the terminal.</p>
|
||||
<p>The fields of the struct appear exactly as named. This is not the typical JSON style that you may expect, though, which uses camel casing for names of fields. You’ll change the names of the field to follow camel case style in this next example. As you’ll see when you run this example, this won’t work because the desired field names conflict with Go’s rules about exported field names.</p>
|
||||
<div class="codehilite"><pre><span></span><code><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w"></span>
|
||||
|
||||
<span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="s">"encoding/json"</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="s">"fmt"</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="s">"log"</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="s">"os"</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="s">"time"</span><span class="w"></span>
|
||||
<span class="p">)</span><span class="w"></span>
|
||||
|
||||
<span class="kd">type</span><span class="w"> </span><span class="nx">User</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">name</span><span class="w"> </span><span class="kt">string</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">password</span><span class="w"> </span><span class="kt">string</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">preferredFish</span><span class="w"> </span><span class="p">[]</span><span class="kt">string</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">createdAt</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Time</span><span class="w"></span>
|
||||
<span class="p">}</span><span class="w"></span>
|
||||
|
||||
<span class="kd">func</span><span class="w"> </span><span class="nx">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">u</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="o">&</span><span class="nx">User</span><span class="p">{</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">name</span><span class="p">:</span><span class="w"> </span><span class="s">"Sammy the Shark"</span><span class="p">,</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">password</span><span class="p">:</span><span class="w"> </span><span class="s">"fisharegreat"</span><span class="p">,</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">createdAt</span><span class="p">:</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Now</span><span class="p">(),</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="p">}</span><span class="w"></span>
|
||||
|
||||
<span class="w"> </span><span class="nx">out</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">json</span><span class="p">.</span><span class="nx">MarshalIndent</span><span class="p">(</span><span class="nx">u</span><span class="p">,</span><span class="w"> </span><span class="s">""</span><span class="p">,</span><span class="w"> </span><span class="s">" "</span><span class="p">)</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">log</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">os</span><span class="p">.</span><span class="nx">Exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="p">}</span><span class="w"></span>
|
||||
|
||||
<span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nb">string</span><span class="p">(</span><span class="nx">out</span><span class="p">))</span><span class="w"></span>
|
||||
<span class="p">}</span><span class="w"></span>
|
||||
</code></pre></div>
|
||||
|
||||
<p>This will present the following output:</p>
|
||||
<div class="codehilite"><pre><span></span><code><span class="k">[secondary_label Output]</span><span class="w"></span>
|
||||
<span class="na">{}</span><span class="w"></span>
|
||||
</code></pre></div>
|
||||
|
||||
<p>In this version, we’ve altered the names of the fields to be camel cased. Now <code>Name</code> is <code>name</code>, <code>Password</code> is <code>password</code>, and finally <code>CreatedAt</code> is <code>createdAt</code>. Within the body of <code>main</code> we’ve changed the instantiation of our struct to use these new names. We then pass the struct to the <code>json.MarshalIndent</code> function as before. The output, this time is an empty JSON object, <code>{}</code>.</p>
|
||||
<p>Camel casing fields properly requires that the first character be lower-cased. While JSON doesn’t care how you name your fields, Go does, as it indicates the visibility of the field outside of the package. Since the <code>encoding/json</code> package is a separate package from the <code>main</code> package we’re using, we must uppercase the first character in order to make it visible to <code>encoding/json</code>. It would seem that we’re at an impasse. We need some way to convey to the JSON encoder what we would like this field to be named.</p>
|
||||
<h3 id="using-struct-tags-to-control-encoding">Using Struct Tags to Control Encoding</h3>
|
||||
<p>You can modify the previous example to have exported fields that are properly encoded with camel-cased field names by annotating each field with a struct tag. The struct tag that <code>encoding/json</code> recognizes has a key of <code>json</code> and a value that controls the output. By placing the camel-cased version of the field names as the value to the <code>json</code> key, the encoder will use that name instead. This example fixes the previous two attempts:</p>
|
||||
<div class="codehilite"><pre><span></span><code><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w"></span>
|
||||
|
||||
<span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="s">"encoding/json"</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="s">"fmt"</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="s">"log"</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="s">"os"</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="s">"time"</span><span class="w"></span>
|
||||
<span class="p">)</span><span class="w"></span>
|
||||
|
||||
<span class="kd">type</span><span class="w"> </span><span class="nx">User</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">Name</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p"><^></span><span class="s">`json:"name"`</span><span class="p"><^></span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">Password</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="p"><^></span><span class="s">`json:"password"`</span><span class="p"><^></span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">PreferredFish</span><span class="w"> </span><span class="p">[]</span><span class="kt">string</span><span class="w"> </span><span class="p"><^></span><span class="s">`json:"preferredFish"`</span><span class="p"><^></span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">CreatedAt</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Time</span><span class="w"> </span><span class="p"><^></span><span class="s">`json:"createdAt"`</span><span class="p"><^></span><span class="w"></span>
|
||||
<span class="p">}</span><span class="w"></span>
|
||||
|
||||
<span class="kd">func</span><span class="w"> </span><span class="nx">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">u</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="o">&</span><span class="nx">User</span><span class="p">{</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">Name</span><span class="p">:</span><span class="w"> </span><span class="s">"Sammy the Shark"</span><span class="p">,</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">Password</span><span class="p">:</span><span class="w"> </span><span class="s">"fisharegreat"</span><span class="p">,</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">CreatedAt</span><span class="p">:</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Now</span><span class="p">(),</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="p">}</span><span class="w"></span>
|
||||
|
||||
<span class="w"> </span><span class="nx">out</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">json</span><span class="p">.</span><span class="nx">MarshalIndent</span><span class="p">(</span><span class="nx">u</span><span class="p">,</span><span class="w"> </span><span class="s">""</span><span class="p">,</span><span class="w"> </span><span class="s">" "</span><span class="p">)</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">log</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">os</span><span class="p">.</span><span class="nx">Exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="p">}</span><span class="w"></span>
|
||||
|
||||
<span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nb">string</span><span class="p">(</span><span class="nx">out</span><span class="p">))</span><span class="w"></span>
|
||||
<span class="p">}</span><span class="w"></span>
|
||||
</code></pre></div>
|
||||
|
||||
<p>This will output:</p>
|
||||
<div class="codehilite"><pre><span></span><code><span class="k">[secondary_label Output]</span><span class="w"></span>
|
||||
<span class="na">{</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="na">"name": "Sammy the Shark",</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="na">"password": "fisharegreat",</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="na">"preferredFish": null,</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="na">"createdAt": "2019-09-23T18:16:17.57739-04:00"</span><span class="w"></span>
|
||||
<span class="na">}</span><span class="w"></span>
|
||||
</code></pre></div>
|
||||
|
||||
<p>We’ve changed the struct fields back to be visible to other packages by capitalizing the first letters of their names. However, this time we’ve added struct tags in the form of <code>json:"name"</code>, where <code>"name"</code> was the name we wanted <code>json.MarshalIndent</code> to use when printing our struct as JSON.</p>
|
||||
<p>We’ve now successfully formatted our JSON correctly. Notice, however, that the fields for some values were printed even though we did not set those values. The JSON encoder can eliminate these fields as well, if you like.</p>
|
||||
<h3 id="removing-empty-json-fields">Removing Empty JSON Fields</h3>
|
||||
<p>It is common to suppress outputting fields that are unset in JSON. Since all types in Go have a “zero value,” some default value that they are set to, the <code>encoding/json</code> package needs additional information to be able to tell that some field should be considered unset when it assumes this zero value. Within the value part of any <code>json</code> struct tag, you can suffix the desired name of your field with <code>,omitempty</code> to tell the JSON encoder to suppress the output of this field when the field is set to the zero value. The following example fixes the previous examples to no longer output empty fields:</p>
|
||||
<div class="codehilite"><pre><span></span><code><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w"></span>
|
||||
|
||||
<span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="s">"encoding/json"</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="s">"fmt"</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="s">"log"</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="s">"os"</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="s">"time"</span><span class="w"></span>
|
||||
<span class="p">)</span><span class="w"></span>
|
||||
|
||||
<span class="kd">type</span><span class="w"> </span><span class="nx">User</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">Name</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="s">`json:"name"`</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">Password</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="s">`json:"password"`</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">PreferredFish</span><span class="w"> </span><span class="p">[]</span><span class="kt">string</span><span class="w"> </span><span class="s">`json:"preferredFish<^>,omitempty<^>"`</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">CreatedAt</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Time</span><span class="w"> </span><span class="s">`json:"createdAt"`</span><span class="w"></span>
|
||||
<span class="p">}</span><span class="w"></span>
|
||||
|
||||
<span class="kd">func</span><span class="w"> </span><span class="nx">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">u</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="o">&</span><span class="nx">User</span><span class="p">{</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">Name</span><span class="p">:</span><span class="w"> </span><span class="s">"Sammy the Shark"</span><span class="p">,</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">Password</span><span class="p">:</span><span class="w"> </span><span class="s">"fisharegreat"</span><span class="p">,</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">CreatedAt</span><span class="p">:</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Now</span><span class="p">(),</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="p">}</span><span class="w"></span>
|
||||
|
||||
<span class="w"> </span><span class="nx">out</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">json</span><span class="p">.</span><span class="nx">MarshalIndent</span><span class="p">(</span><span class="nx">u</span><span class="p">,</span><span class="w"> </span><span class="s">""</span><span class="p">,</span><span class="w"> </span><span class="s">" "</span><span class="p">)</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">log</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">os</span><span class="p">.</span><span class="nx">Exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="p">}</span><span class="w"></span>
|
||||
|
||||
<span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nb">string</span><span class="p">(</span><span class="nx">out</span><span class="p">))</span><span class="w"></span>
|
||||
<span class="p">}</span><span class="w"></span>
|
||||
</code></pre></div>
|
||||
|
||||
<p>This example will output:</p>
|
||||
<div class="codehilite"><pre><span></span><code><span class="k">[secondary_label Output]</span><span class="w"></span>
|
||||
<span class="na">{</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="na">"name": "Sammy the Shark",</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="na">"password": "fisharegreat",</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="na">"createdAt": "2019-09-23T18:21:53.863846-04:00"</span><span class="w"></span>
|
||||
<span class="na">}</span><span class="w"></span>
|
||||
</code></pre></div>
|
||||
|
||||
<p>We’ve modified the previous examples so that the <code>PreferredFish</code> field now has the struct tag <code>json:"preferredFish,omitempty"</code>. The presence of the <code>,omitempty</code> augmentation causes the JSON encoder to skip that field, since we decided to leave it unset. This had the value <code>null</code> in our previous examples’ outputs.</p>
|
||||
<p>This output is looking much better, but we’re still printing out the user’s password. The <code>encoding/json</code> package provides another way for us to ignore private fields entirely.</p>
|
||||
<h3 id="ignoring-private-fields">Ignoring Private Fields</h3>
|
||||
<p>Some fields must be exported from structs so that other packages can correctly interact with the type. However, the nature of these fields may be sensitive, so in these circumstances, we would like the JSON encoder to ignore the field entirely—even when it is set. This is done using the special value <code>-</code> as the value argument to a <code>json:</code> struct tag.</p>
|
||||
<p>This example fixes the issue of exposing the user’s password.</p>
|
||||
<div class="codehilite"><pre><span></span><code><span class="kn">package</span><span class="w"> </span><span class="nx">main</span><span class="w"></span>
|
||||
|
||||
<span class="kn">import</span><span class="w"> </span><span class="p">(</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="s">"encoding/json"</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="s">"fmt"</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="s">"log"</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="s">"os"</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="s">"time"</span><span class="w"></span>
|
||||
<span class="p">)</span><span class="w"></span>
|
||||
|
||||
<span class="kd">type</span><span class="w"> </span><span class="nx">User</span><span class="w"> </span><span class="kd">struct</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">Name</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="s">`json:"name"`</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">Password</span><span class="w"> </span><span class="kt">string</span><span class="w"> </span><span class="s">`json:"<^>-<^>"`</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">CreatedAt</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Time</span><span class="w"> </span><span class="s">`json:"createdAt"`</span><span class="w"></span>
|
||||
<span class="p">}</span><span class="w"></span>
|
||||
|
||||
<span class="kd">func</span><span class="w"> </span><span class="nx">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">u</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="o">&</span><span class="nx">User</span><span class="p">{</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">Name</span><span class="p">:</span><span class="w"> </span><span class="s">"Sammy the Shark"</span><span class="p">,</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">Password</span><span class="p">:</span><span class="w"> </span><span class="s">"fisharegreat"</span><span class="p">,</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">CreatedAt</span><span class="p">:</span><span class="w"> </span><span class="nx">time</span><span class="p">.</span><span class="nx">Now</span><span class="p">(),</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="p">}</span><span class="w"></span>
|
||||
|
||||
<span class="w"> </span><span class="nx">out</span><span class="p">,</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">:=</span><span class="w"> </span><span class="nx">json</span><span class="p">.</span><span class="nx">MarshalIndent</span><span class="p">(</span><span class="nx">u</span><span class="p">,</span><span class="w"> </span><span class="s">""</span><span class="p">,</span><span class="w"> </span><span class="s">" "</span><span class="p">)</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="nx">err</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="kc">nil</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">log</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nx">os</span><span class="p">.</span><span class="nx">Exit</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="p">}</span><span class="w"></span>
|
||||
|
||||
<span class="w"> </span><span class="nx">fmt</span><span class="p">.</span><span class="nx">Println</span><span class="p">(</span><span class="nb">string</span><span class="p">(</span><span class="nx">out</span><span class="p">))</span><span class="w"></span>
|
||||
<span class="p">}</span><span class="w"></span>
|
||||
</code></pre></div>
|
||||
|
||||
<p>When you run this example, you’ll see this output:</p>
|
||||
<div class="codehilite"><pre><span></span><code><span class="k">[secondary_label Output]</span><span class="w"></span>
|
||||
<span class="na">{</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="na">"name": "Sammy the Shark",</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="na">"createdAt": "2019-09-23T16:08:21.124481-04:00"</span><span class="w"></span>
|
||||
<span class="na">}</span><span class="w"></span>
|
||||
</code></pre></div>
|
||||
|
||||
<p>The only thing we’ve changed in this example from previous ones is that the password field now uses the special <code>"-"</code> value for its <code>json:</code> struct tag. In the output from this example that the <code>password</code> field is no longer present.</p>
|
||||
<p>These features of the <code>encoding/json</code> package — <code>,omitempty</code>, <code>"-"</code>, and <a href="https://pkg.go.dev/encoding/json#Marshal">other options</a> — are not standards. What a package decides to do with values of a struct tag depends on its implementation. Because the <code>encoding/json</code> package is part of the standard library, other packages have also implemented these features in the same way as a matter of convention. However, it’s important to read the documentation for any third-party package that uses struct tags to learn what is supported and what is not.</p>
|
||||
<h2 id="conclusion">Conclusion</h2>
|
||||
<p>Struct tags offer a powerful means to augment the functionality of code that works with your structs. Many standard library and third-party packages offer ways to customize their operation through the use of struct tags. Using them effectively in your code provides both this customization behavior and succinctly documents how these fields are used to future developers.</p>
|
||||
</article>
|
||||
<footer>
|
||||
<p>Page generated on 2022-06-28</p>
|
||||
</footer>
|
||||
</main>
|
||||
<script type="text/javascript">
|
||||
document.querySelectorAll('.tag').forEach(tag => {
|
||||
let text = tag.innerText;
|
||||
tag.innerText = '';
|
||||
tag.innerHTML = `<a href="/tags.html#${text}">${text}</a>`;
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,203 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Zk | gitea-2</title>
|
||||
<link rel="stylesheet" type="text/css" href="/style.css" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<meta charset="utf-8" />
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<header>
|
||||
<h1>Zk | gitea-2</h1>
|
||||
</header>
|
||||
<article class="content">
|
||||
<p><a href="https://gitea.io">Gitea</a> is a source code repository based on the version control system, <a href="https://git-scm.org">Git</a>. While there are several self-hosted solutions available such as GitLab and Gogs, Gitea has the benefit of being light-weight and easy to run on a small server.</p>
|
||||
<p>However, having a small server, especially in the realm of VPSes, often means being limited on space. Thankfully, many hosting providers also offer additional storage in the form of block storage or networked file storage (NFS). This gives small and medium businesses the option to save money on smaller VPS hosts for their applications while not sacrificing storage.</p>
|
||||
<p>With Gitea and the ability to decide where your source code is stored, it’s easy to ensure that your projects and files have room to expand.</p>
|
||||
<h2 id="prerequisites">Prerequisites</h2>
|
||||
<p>Before you get started, you will need the following:</p>
|
||||
<ul>
|
||||
<li>A server running Ubuntu 20.04</li>
|
||||
<li>An installation of Gitea. If you’d like a quick way to get started, you can see this tutorial on <a href="link-goes-here.html">Installing Gitea on Ubuntu 20.04 Using Docker</a>. <!-- TODO --></li>
|
||||
<li>An additional Linux volume; for example, DigitalOcean’s <a href="https://docs.digitalocean.com/products/volumes/">block storage volumes</a> attached to a Droplet.</li>
|
||||
</ul>
|
||||
<h2 id="step-1-mounting-a-linux-volume">Step 1 — Mounting a Linux Volume</h2>
|
||||
<p>A volume can take many different forms. It could be an NFS volume, which is storage available on the network provided via a file share. Another possibility is that it takes the form of block storage via a service such as DigitalOcean’s Volumes or Amazon’s EBS. In both cases, storage is mounted on a system using the <code>mount</code> command.</p>
|
||||
<p>These volumes will be visible as a device, files which live in <code>/dev</code>. These files are how the kernel communicates with the storage devices themselves, not actually used for storage. In order to be able to store files on the storage device, you will need to <strong>mount</strong> them using the <code>mount</code> command.</p>
|
||||
<p>First, you will need to create a <strong>mount point</strong> — that is, a folder which will be associated with the device, such that data stored within it winds up stored on that device. Mount points for storage devices such as this typically live in the <code>/mnt</code> directory.</p>
|
||||
<p>Create a mount point named <code>gitea</code> as you would create a normal directory using the <code>mkdir</code> command:</p>
|
||||
<div class="codehilite"><pre><span></span><code>sudo mkdir /mnt/gitea
|
||||
</code></pre></div>
|
||||
|
||||
<p>From here, we can mount the device to that directory in order to use it to access that storage space. Use the <code>mount</code> command to mount the device:</p>
|
||||
<div class="codehilite"><pre><span></span><code>sudo mount -o defaults,noatime /dev/disk/by-id/<^>your_disk_id<^> /mnt/gitea
|
||||
</code></pre></div>
|
||||
|
||||
<p>This command mounts the device specified by its ID to <code>/mnt/gitea</code>. The <code>-o</code> option specifies the options used when mounting. In this case, you are using the default options which allow for mounting a read/write file system, and the <code>noatime</code> option specifies that the kernel shouldn’t update the last access time for files and directories on the device in order to be more efficient.</p>
|
||||
<p>Now that you’ve mounted your device, it will stay mounted as long as the system is up and running. However, as soon as the system restarts, it will no longer be mounted (though the data will remain on the volume), so you will need to tell the system to mount the volume as soon as it starts using the <code>/etc/fstab</code> (‘file systems table’) file, which lists the available file systems and their mount points in a simple tab-delimited format.</p>
|
||||
<p>Using <code>echo</code> and <code>tee</code>, add a new line to the end of <code>/etc/fstab</code>:</p>
|
||||
<div class="codehilite"><pre><span></span><code>echo '/dev/disk/by-id/<^>your_disk_id<^> /mnt/gitea ext4 defaults,nofail,noatime 0 0' | sudo tee /etc/fstab
|
||||
</code></pre></div>
|
||||
|
||||
<p>This command appends the string <code>/dev/disk/by-uid/<^>your_disk_id<^></code> to the fstab file and to your screen. As with the <code>mount</code> command above, it mounts the device onto the mount point using the <code>defaults</code>, <code>nofail</code>, and <code>noatime</code> options. The string <code>ext4</code> between the mount point and options specifies the file system type, ext4 in this case, though depending on your volume’s file system type, it may be something like xfs or nfs; to check which type your volume uses, run the <code>mount</code> command with no options:</p>
|
||||
<div class="codehilite"><pre><span></span><code>mount
|
||||
</code></pre></div>
|
||||
|
||||
<p>This will provide you with a line of output for every mounted file system. Since you just mounted yours, it will likely be the last on the list, and look something like this:</p>
|
||||
<div class="codehilite"><pre><span></span><code>/dev/sda on /mnt/gitea type ext4 (rw,noatime,discard)
|
||||
</code></pre></div>
|
||||
|
||||
<p>which shows that the file system type is <code>ext4</code>.</p>
|
||||
<p>Once your changes have been made to <code>/etc/fstab</code>, the kernel will mount your Linux volume on boot.</p>
|
||||
<p><$>[note]
|
||||
<strong>Note:</strong> Storage devices on Linux are very flexible and come in all different types, from a networked file system (NFS) to a plain old hard drive. To learn more about block storage and devices in Linux, you can read up more about storage concepts <a href="https://www.digitalocean.com/community/tutorials/an-introduction-to-storage-terminology-and-concepts-in-linux">here</a>.
|
||||
<$></p>
|
||||
<h2 id="step-2-configuring-gitea-to-store-data-on-a-linux-volume">Step 2 — Configuring Gitea to Store Data on a Linux Volume</h2>
|
||||
<p>Gitea maintains all of its repositories in a central location. This includes repositories from all users and organizations. Unless configured otherwise, all information is kept in a single directory. This directory is named <code>data</code> in default installations. For the purposes of this demo, we will be using a version of Gitea running on Docker as in the tutorial linked above.</p>
|
||||
<p>First of all, let’s see what this data directory contains. You can do this by moving to the data directory and running the <code>ls</code> command. Using the <code>-l</code> format will tell us more information about the files:</p>
|
||||
<div class="codehilite"><pre><span></span><code>cd gitea
|
||||
ls -l
|
||||
</code></pre></div>
|
||||
|
||||
<p>This should provide a listing that looks something like this:</p>
|
||||
<div class="codehilite"><pre><span></span><code><span class="k">[secondary_label Output]</span><span class="w"></span>
|
||||
<span class="na">total 20</span><span class="w"></span>
|
||||
<span class="na">drwxr-xr-x 5 root root 4096 Jun 23 22:34 ./</span><span class="w"></span>
|
||||
<span class="na">drwxrwxr-x 3 sammy sammy 4096 Jun 26 22:35 ../</span><span class="w"></span>
|
||||
<span class="na">drwxr-xr-x 5 git git 4096 Jun 23 22:42 git/</span><span class="w"></span>
|
||||
<span class="na">drwxr-xr-x 12 git git 4096 Jun 26 22:35 gitea/</span><span class="w"></span>
|
||||
<span class="na">drwx------ 2 root root 4096 Jun 23 22:34 ssh/</span><span class="w"></span>
|
||||
</code></pre></div>
|
||||
|
||||
<p>Let’s break down the output of this command. It lists one file or directory per line. In this case, it lists five directories. The entry for <code>.</code> is a special entry that just means the current directory, and <code>..</code> stands for the directory one level up. You can see that the current directory is owned by the <code>root</code> user, which is the case in this instance because Docker runs as a privileged user, and the directory one level up is owned by <code>sammy</code>.</p>
|
||||
<p>The <code>git</code> directory is important to us because it contains all of the repositories that we might want to store on a separate volume. Within it are two directories of note: the <code>repositories</code> directory contains the git repositories managed by Gitea sorted by user/organization, and the <code>lfs</code> directory contains data for Git’s Large File Storage functionality. The <code>gitea</code> directory contains plenty of information that Gitea uses in the background, including archives of old repositories, as well the database that contains information such as users and repository information used by the web service. The <code>ssh</code> directory contains various SSH keypairs that are used in Gitea’s operation.</p>
|
||||
<p>Given that all of the information stored in this directory is important to us, we will want to include the entire directory’s contents on our attached volume.</p>
|
||||
<h3 id="setting-up-a-new-installation-of-gitea">Setting Up a New Installation of Gitea</h3>
|
||||
<p>If you are starting with a brand new installation of Gitea, you can specify where all of your information is stored during the configuration process. For example, if you are setting Gitea up using Docker Compose, you can map the volumes to your attached volume.</p>
|
||||
<p>Open up the <code>docker-compose.yml</code> file with your preferred text editor. The following example uses <code>nano</code>. Search for the <code>volumes</code> entry in the compose file and modify the mapping on the left side of the <code>:</code> to point to appropriate locations on your Linux volume for the Gitea data directory:</p>
|
||||
<div class="codehilite"><pre><span></span><code><span class="p p-Indicator">[</span><span class="nv">label docker-compose.yml</span><span class="p p-Indicator">]</span><span class="w"></span>
|
||||
<span class="nn">...</span><span class="w"></span>
|
||||
|
||||
<span class="w"> </span><span class="nt">volumes</span><span class="p">:</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain"><^>/mnt/gitea<^>:/data</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">/home/git/.ssh/:/data/git/.ssh</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">/etc/timezone:/etc/timezone:ro</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">/etc/localtime:/etc/localtime:ro</span><span class="w"></span>
|
||||
|
||||
<span class="nn">...</span><span class="w"></span>
|
||||
</code></pre></div>
|
||||
|
||||
<p><$>[warning]
|
||||
<strong>Warning:</strong> SSH servers look for the <code>.ssh</code> directory, which contains all of the SSH keys that Gitea will use, in the home directory of the Git user (<code>git</code>, in this case), so it is not advised to move the mount for this Docker volume. In order to have this location backed up on your Linux volume, it would be best to use another solution such as a <code>cron</code> job to back up the directory. For more information on using <code>cron</code> to manage scheduled tasks, see <a href="https://www.digitalocean.com/community/tutorials/how-to-use-cron-to-automate-tasks-ubuntu-1804">this tutorial</a>.
|
||||
<$></p>
|
||||
<p>When you run <code>docker-compose</code> and Gitea installs, it will use your Linux volume to store its data.</p>
|
||||
<p>If you are not using Docker volumes to manage the locations of your data — for example, if you are installing Gitea on your server via the binary releases per <a href="https://docs.gitea.io/en-us/install-from-binary/">these instructions from Gitea</a> — then you will need to modify the locations within the configuration file (usually <code>/etc/gitea/app.ini</code>) when you are setting all of the values. For instance, you might set them as follows:</p>
|
||||
<div class="codehilite"><pre><span></span><code><span class="k">[label app.ini]</span><span class="w"></span>
|
||||
<span class="na">...</span><span class="w"></span>
|
||||
|
||||
<span class="c1"># If you are using SQLite for your database, you will need to change the PATH</span><span class="w"></span>
|
||||
<span class="c1"># variable in this section</span><span class="w"></span>
|
||||
<span class="k">[database]</span><span class="w"></span>
|
||||
<span class="na">...</span><span class="w"></span>
|
||||
<span class="na">PATH</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s"><^>/mnt/gitea<^>/gitea.db</span><span class="w"></span>
|
||||
|
||||
<span class="k">[server]</span><span class="w"></span>
|
||||
<span class="na">...</span><span class="w"></span>
|
||||
<span class="na">LFS_CONTENT_PATH</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s"><^>/mnt/gitea<^>/lfs</span><span class="w"></span>
|
||||
|
||||
<span class="k">[repository]</span><span class="w"></span>
|
||||
<span class="na">ROOT</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s"><^>/mnt/gitea<^>/gitea-repositories</span><span class="w"></span>
|
||||
|
||||
<span class="na">...</span><span class="w"></span>
|
||||
</code></pre></div>
|
||||
|
||||
<p><$>[note]
|
||||
<strong>Note:</strong> Ensure that your git user has write access to this location. You can read up on Linux permissions <a href="https://www.digitalocean.com/community/tutorials/an-introduction-to-linux-permissions">here</a>.
|
||||
<$></p>
|
||||
<h3 id="moving-an-existing-installation-of-gitea">Moving an Existing Installation of Gitea</h3>
|
||||
<p>If you already have an instance of Gitea installed and running, you will still be able to store your data on a separate Linux volume, but it will require some care in ensuring that all of your data remains both safe and accessible to Gitea.</p>
|
||||
<p><$>[note]
|
||||
<strong>Note:</strong> As with any operation involving your data, it is important to ensure that you have an up-to-date backup of everything. In this case, this means a back up of all of the files in your data directory (the SSH files, the <code>gitea-repositories</code> and <code>lfs</code> directories, the database, and so on) to a safe location.
|
||||
<$></p>
|
||||
<p>There are two options for moving your data to a new volume. The first way is to copy your Gitea data to a secondary location, then turn the original location into a mount point for your Linux volume. When you copy your data back into that location, you will be copying it onto that volume, and no changes will be required within Gitea itself; it will simply continue as it did before. If, however, you do not want to mount the entire device to that destination (for example, if your Gitea data will not be the only thing on that volume), then a second option is to move all of your Gitea data to a new location on that volume and instruct Gitea itself to use that location.</p>
|
||||
<p>No matter which option you choose, first, stop the Gitea web service.</p>
|
||||
<p>If you are using Gitea installed via Docker Compose, use <code>docker-compose</code> to stop the service. While inside the same directory containing the <code>docker-compose.yml</code> file, run:</p>
|
||||
<div class="codehilite"><pre><span></span><code>docker-compose down
|
||||
</code></pre></div>
|
||||
|
||||
<p>If you have installed it as a Linux service, use the <code>systemctl</code> command:</p>
|
||||
<div class="codehilite"><pre><span></span><code>sudo systemctl stop gitea
|
||||
</code></pre></div>
|
||||
|
||||
<p>Once Gitea has been stopped, move the entire contents of the data directory to the mount point made in step 1:</p>
|
||||
<div class="codehilite"><pre><span></span><code>mv * /mnt/gitea
|
||||
</code></pre></div>
|
||||
|
||||
<p>Ensure that all files have been moved by listing all of the current directory’s contents:</p>
|
||||
<div class="codehilite"><pre><span></span><code>ls -la
|
||||
</code></pre></div>
|
||||
|
||||
<p>You should only see the current and parent directory entries. The result should look something like this:</p>
|
||||
<div class="codehilite"><pre><span></span><code>total 8
|
||||
drwxrwxr-x 2 mscottclary mscottclary 4096 Jun 27 13:56 ./
|
||||
drwxr-xr-x 7 mscottclary mscottclary 4096 Jun 27 13:56 ../
|
||||
</code></pre></div>
|
||||
|
||||
<p>Once all of the data has been moved, change to that directory:</p>
|
||||
<div class="codehilite"><pre><span></span><code>cd /mnt/gitea
|
||||
</code></pre></div>
|
||||
|
||||
<p>Using <code>ls</code>, ensure that everything looks correct. As before, you should see the <code>ssh</code>, <code>git</code>, and <code>gitea</code> directories. If you are using SQLite as a database to manage Gitea, you should also see a file named <code>gitea.db</code> in the <code>gitea</code> directory.</p>
|
||||
<p>When you’re sure that all data has been moved, it’s time to mount the Linux volume to the data directory. </p>
|
||||
<p>First, move to the parent directory of the data directory.</p>
|
||||
<div class="codehilite"><pre><span></span><code>cd <^>/home/sammy/gitea/..<^>
|
||||
</code></pre></div>
|
||||
|
||||
<p>As before, use the <code>mount</code> command, but this time, use the directory you just emptied as the destination:</p>
|
||||
<div class="codehilite"><pre><span></span><code>sudo mount -o defaults,noatime /dev/disk/by-id/<^>your_disk_id<^> gitea
|
||||
</code></pre></div>
|
||||
|
||||
<p>Now, when you look inside that directory, you should see all of your files in place:</p>
|
||||
<div class="codehilite"><pre><span></span><code>ls -la gitea
|
||||
</code></pre></div>
|
||||
|
||||
<p>This command should output the expected information. Note that, depending on your volume’s file system type, you may see an additional directory named <code>lost+found</code>; this is normal and part of everyday file system use.</p>
|
||||
<div class="codehilite"><pre><span></span><code>total 36
|
||||
drwxr-xr-x 6 root root 4096 Jun 27 13:58 ./
|
||||
drwxrwxr-x 3 mscottclary mscottclary 4096 Jun 27 02:23 ../
|
||||
drwxr-xr-x 5 git git 4096 Jun 23 22:42 git/
|
||||
drwxr-xr-x 12 git git 4096 Jun 27 00:00 gitea/
|
||||
drwx------ 2 root root 16384 Jun 27 03:46 lost+found/
|
||||
drwx------ 2 root root 4096 Jun 23 22:34 ssh/
|
||||
</code></pre></div>
|
||||
|
||||
<p>As mentioned, if you would like Gitea to use a directory within the Linux volume, there is an additional step you need to complete before bringing Gitea back up. For example, say that you want to use a folder named <code>scm</code> on your volume mounted on <code>/mnt/gitea</code>. After moving all of your Gitea data to <code>/mnt/gitea/scm</code>, you will need to create a symbolic link from your old data directory to the new one. For this you will use the <code>ln</code> command:</p>
|
||||
<div class="codehilite"><pre><span></span><code>sudo ln -s /mnt/gitea/scm gitea
|
||||
</code></pre></div>
|
||||
|
||||
<p>At this point, you can restart Gitea. If you are using Gitea as a Linux service, run:</p>
|
||||
<div class="codehilite"><pre><span></span><code>sudo systemctl restart gitea
|
||||
</code></pre></div>
|
||||
|
||||
<p>If you are running Gitea as a Docker container using Docker Compose, run:</p>
|
||||
<div class="codehilite"><pre><span></span><code>docker-compose up -d
|
||||
</code></pre></div>
|
||||
|
||||
<p>Now that everything is up and running, visit your Gitea instance in the browser and ensure that everything works as expected. You should be able to create new objects in Gitea such as repositories, issues, and so on. If you set Gitea up with an SSH shim, you should also be able to check out and push to repositories using <code>git clone</code> and <code>git push</code>.</p>
|
||||
<h2 id="conclusion">Conclusion</h2>
|
||||
<p>In this tutorial, you moved all of your Gitea data to a Linux volume. Volumes such as these are very flexible and provide many benefits.such as allowing you to store all of your data on larger disks, RAID volumes, networked file systems, or using block storage such as DigitalOcean Volumes or Amazon EBS to reduce storage expenses. It also allows you to snapshot entire disks for backup so that you can restore their contents in event of a catastrophic failure.</p>
|
||||
</article>
|
||||
<footer>
|
||||
<p>Page generated on 2022-06-28</p>
|
||||
</footer>
|
||||
</main>
|
||||
<script type="text/javascript">
|
||||
document.querySelectorAll('.tag').forEach(tag => {
|
||||
let text = tag.innerText;
|
||||
tag.innerText = '';
|
||||
tag.innerHTML = `<a href="/tags.html#${text}">${text}</a>`;
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,279 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Zk | gitea-3</title>
|
||||
<link rel="stylesheet" type="text/css" href="/style.css" />
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<meta charset="utf-8" />
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<header>
|
||||
<h1>Zk | gitea-3</h1>
|
||||
</header>
|
||||
<article class="content">
|
||||
<h1 id="how-to-deploy-a-static-website-from-gitea-using-git-hooks">How To Deploy a Static Website from Gitea Using Git Hooks</h1>
|
||||
<h3 id="introduction">Introduction</h3>
|
||||
<p>The popular source code management system <a href="https://git-scm.com">Git</a> is quite flexible when it comes to allowing you to perform various tasks when actions are taken on a repository. For example, you might want to send an email when a feature branch is merged into your main branch, or log various Git actions to a file for tracking.</p>
|
||||
<p>These actions are called <strong>hooks</strong>, and there are several that you can use for various steps along the way to perform additional work within your Git workflow. For example:</p>
|
||||
<ul>
|
||||
<li><code>post-receive</code> hooks can be used to perform actions when a commit is pushed to the repository and are useful for CI/CD.</li>
|
||||
<li><code>post-merge</code> hooks can be used to perform actions after a merge has been completed and are useful for logging and notifications.</li>
|
||||
<li><code>post-checkout</code> hooks can be used to perform actions after you run <code>git checkout</code> and are useful for setting up project environments.</li>
|
||||
</ul>
|
||||
<p>For this tutorial, we will be looking at using the <code>post-receive</code> hook. A common usage for the hook is running some form of continuous integration or deployment. This might mean running tests or deploying a project. For this example, we will be automatically deploying changes to a static HTML site served by Nginx.</p>
|
||||
<h2 id="prerequisites">Prerequisites</h2>
|
||||
<p>Gitea is a lightweight and flexible source code management system that will be used for the examples here, so before beginning this tutorial, you should have the following:</p>
|
||||
<ul>
|
||||
<li>An Ubuntu 20.04 server with a non-root user configured with <code>sudo</code> privileges as described in the <a href="https://www.digitalocean.com/community/tutorials/initial-server-setup-with-ubuntu-20-04">Initial server setup for Ubuntu 20.04</a> tutorial.</li>
|
||||
<li>Docker installed on your server as described by <strong>Steps 1 and 2</strong> of our guide on <a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-20-04">How to Install Docker on Ubuntu 20.04</a>.</li>
|
||||
<li>Docker Compose installed on your server. Follow <strong>Step 1</strong> of our guide on <a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-compose-on-ubuntu-20-04#step-1-installing-docker-compose">How to Install and Use Docker Compose on Ubuntu 20.04</a>.</li>
|
||||
<li>A domain name pointed at your server. If you are using a DigitalOcean Droplet, you can accomplish this by following our <a href="https://docs.digitalocean.com/products/networking/dns/">Domains and DNS</a> documentation. This tutorial will use <^>your_domain<^> in examples throughout.</li>
|
||||
<li>Gitea installed on the server behind Nginx, ready to interact with using Git over SSH as described in <a href="https://www.digitalocean.com/community/tutorials/how-to-install-gitea-on-ubuntu-using-docker">How To Install Gitea on Ubuntu Using Docker</a>.</li>
|
||||
</ul>
|
||||
<h2 id="step-1-creating-a-static-html-project-in-gitea">Step 1 — Creating a Static HTML Project in Gitea</h2>
|
||||
<p>To begin with, you will need a repository set up in Gitea. Create a new repository using the <strong>+</strong> button in the upper right corner of the Gitea page. Name it something memorable such as the domain name that it will be deployed to. Fill out the rest of the new repository information and click <strong>Create Repository</strong>.</p>
|
||||
<p><a href="TODO.html"><img alt="Creating a new repository" src="TODO" /></a></p>
|
||||
<p>For this tutorial, you’ll be setting up a static site at <code>static.<^>your_domain<^></code>, but be sure to replace this with your final site’s domain. Now, wherever you plan on developing, check out that repository using <code>git clone</code>:</p>
|
||||
<div class="codehilite"><pre><span></span><code>git clone git@<^>your_domain<^>:<^>username<^>/static.<^>your_domain<^>
|
||||
</code></pre></div>
|
||||
|
||||
<p>Move into that directory using <code>cd</code>:</p>
|
||||
<div class="codehilite"><pre><span></span><code>cd static.<^>your_domain<^>
|
||||
</code></pre></div>
|
||||
|
||||
<p>Now, create a file named <code>index.html</code> which will be served by Nginx. The following example uses <code>nano</code>. This file will contain a little bit of text that we can view in the browser.</p>
|
||||
<div class="codehilite"><pre><span></span><code>nano index.html
|
||||
</code></pre></div>
|
||||
|
||||
<p>Within that file, add a snippet of HTML that you will recognize:</p>
|
||||
<div class="codehilite"><pre><span></span><code>[label index.html]
|
||||
<span class="p"><</span><span class="nt">html</span><span class="p">></span>
|
||||
<span class="p"><</span><span class="nt">head</span><span class="p">></span>
|
||||
<span class="p"><</span><span class="nt">title</span><span class="p">></span>Testing Gitea<span class="p"></</span><span class="nt">title</span><span class="p">></span>
|
||||
<span class="p"></</span><span class="nt">head</span><span class="p">></span>
|
||||
<span class="p"><</span><span class="nt">body</span><span class="p">></span>
|
||||
<span class="p"><</span><span class="nt">h1</span><span class="p">></span>It works!<span class="p"></</span><span class="nt">h1</span><span class="p">></span>
|
||||
<span class="p"></</span><span class="nt">body</span><span class="p">></span>
|
||||
<span class="p"></</span><span class="nt">html</span><span class="p">></span>
|
||||
</code></pre></div>
|
||||
|
||||
<p>Save and close the file. If you used <code>nano</code> to edit the file, you can do so by pressing <code>Ctrl + X</code>, <code>Y</code>, and then <code>Enter</code>.</p>
|
||||
<p>Now, add the file to the git working tree:</p>
|
||||
<div class="codehilite"><pre><span></span><code>git add index.html
|
||||
</code></pre></div>
|
||||
|
||||
<p>Now that the file is added, commit your changes:</p>
|
||||
<div class="codehilite"><pre><span></span><code>git commit -m "Added index.html"
|
||||
</code></pre></div>
|
||||
|
||||
<p>Push your changes up to your Gitea repository:</p>
|
||||
<div class="codehilite"><pre><span></span><code>git push origin main
|
||||
</code></pre></div>
|
||||
|
||||
<p><$>[note]
|
||||
<strong>Note:</strong> In this example, we are working on the branch named <strong>main</strong>. Your default branch may be named something else such as <strong>master</strong>. In order to find out the name of your current branch, you can run <code>git branch</code>.
|
||||
<$></p>
|
||||
<h2 id="step-2-creating-a-static-html-site-in-nginx">Step 2 — Creating a Static HTML Site in Nginx</h2>
|
||||
<p>Now, let’s set up Nginx to serve our static site. Create a new file in <code>/etc/nginx/sites-available</code> using your text editor:</p>
|
||||
<div class="codehilite"><pre><span></span><code>sudo nano /etc/nginx/sites-available/static.<^>your_domain<^>
|
||||
</code></pre></div>
|
||||
|
||||
<p>In this file, enter the following:</p>
|
||||
<div class="codehilite"><pre><span></span><code><span class="k">[label</span><span class="w"> </span><span class="s">/etc/nginx/sites-available/static.<^>your_domain<^>]</span><span class="w"></span>
|
||||
<span class="s">server</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="kn">server_name</span><span class="w"> </span><span class="s">static.<^>your_domain<^></span><span class="p">;</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="kn">listen</span><span class="w"> </span><span class="mi">80</span><span class="p">;</span><span class="w"></span>
|
||||
|
||||
<span class="w"> </span><span class="kn">root</span><span class="w"> </span><span class="s">/var/www/static.<^>your_domain<^></span><span class="p">;</span><span class="w"></span>
|
||||
|
||||
<span class="w"> </span><span class="kn">index</span><span class="w"> </span><span class="s">index.html</span><span class="p">;</span><span class="w"></span>
|
||||
|
||||
<span class="w"> </span><span class="kn">location</span><span class="w"> </span><span class="s">/</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="kn">try_files</span><span class="w"> </span><span class="nv">$uri</span><span class="w"> </span><span class="nv">$uri/</span><span class="w"> </span><span class="p">=</span><span class="mi">404</span><span class="p">;</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="p">}</span><span class="w"></span>
|
||||
|
||||
<span class="w"> </span><span class="kn">location</span><span class="w"> </span><span class="s">/.git</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="kn">return</span><span class="w"> </span><span class="mi">404</span><span class="p">;</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="p">}</span><span class="w"></span>
|
||||
<span class="p">}</span><span class="w"></span>
|
||||
</code></pre></div>
|
||||
|
||||
<p>This <code>server</code> block instructs Nginx to listen on the standard HTTP port 80 for requests to <code>static.<^>your_domain<^></code>. When it receives a request, it tries to find a file matching that request in <code>/var/www/static.<^>your_domain<^></code>. It will first try looking for the file named (for instance, if you visit <code>static.<^>your_domain<^>/shark.jpg</code>, it will try to find <code>/var/www/static.<^>your_domain<^>/shark.jpg</code>). If that does not exist, it will append a <code>/</code> to the end of the file name to serve a directory or the file named <code>index.html</code>. Finally, if none of those work, it will serve a 404 Not Found error.</p>
|
||||
<p>We also don’t want anyone accessing our <code>.git</code> directory, so the next <code>location</code> block instructs Nginx to return a 404 should someone try.</p>
|
||||
<p>Now, link this file from <code>sites-available</code> to <code>sites-enabled</code>:</p>
|
||||
<div class="codehilite"><pre><span></span><code>sudo ln -s /etc/nginx/sites-available/static.<^>your_domain<^> /etc/nginx/sites-enabled/static.<^>your_domain<^>
|
||||
</code></pre></div>
|
||||
|
||||
<p>In order to make sure everything looks good, you can test the new configuration:</p>
|
||||
<div class="codehilite"><pre><span></span><code>sudo nginx -t
|
||||
</code></pre></div>
|
||||
|
||||
<p>If all has gone well, you should see something like the following:</p>
|
||||
<div class="codehilite"><pre><span></span><code><span class="k">[secondary_label Output]</span><span class="w"></span>
|
||||
<span class="na">nginx: the configuration file /etc/nginx/nginx.conf syntax is ok</span><span class="w"></span>
|
||||
<span class="na">nginx: configuration file /etc/nginx/nginx.conf test is successful</span><span class="w"></span>
|
||||
</code></pre></div>
|
||||
|
||||
<p>Restarting the server will make these changes live.</p>
|
||||
<div class="codehilite"><pre><span></span><code>sudo systemctl restart nginx
|
||||
</code></pre></div>
|
||||
|
||||
<p>We need to create the repository for there to be anything to serve, however. Move to <code>/var/www</code> where you can create the directory for hosting the site:</p>
|
||||
<div class="codehilite"><pre><span></span><code><span class="n">cd</span><span class="w"> </span><span class="o">/</span><span class="k">var</span><span class="o">/</span><span class="n">www</span><span class="w"></span>
|
||||
</code></pre></div>
|
||||
|
||||
<p>Make the directory you specified in your server block above:</p>
|
||||
<div class="codehilite"><pre><span></span><code>sudo mkdir static.<^>your_domain<^>
|
||||
</code></pre></div>
|
||||
|
||||
<p>You will need to change the permissions on this directory so that both you and the <code>git</code> user have write access.</p>
|
||||
<div class="codehilite"><pre><span></span><code>sudo chown <^>username<^>:git static.<^>your_domain<^>
|
||||
sudo chmod g+x static.<^>your_domain<^>
|
||||
</code></pre></div>
|
||||
|
||||
<p>Move to that directory:</p>
|
||||
<div class="codehilite"><pre><span></span><code>cd static.<^>your_domain<^>
|
||||
</code></pre></div>
|
||||
|
||||
<p>Now that you have the directory set up, you can clone the repository into it. Rather than using SSH, which would require a key, you can use the <code>file://</code> protocol for Git. This works because Nginx is running on the same server that the repository lives on. If the repository is public, you can also use the <code>https://</code> protocol.</p>
|
||||
<div class="codehilite"><pre><span></span><code>git clone file:///<^>path_to_gitea_installation<^>/repositories/sammy/static.<^>your_domain<^>.git .
|
||||
</code></pre></div>
|
||||
|
||||
<p>It’s important to remember to add that <code>.</code> at the end of the <code>git clone</code> command, as this tells Git to check out the repository into the current directory rather than making a new one. Now, if you type <code>ls</code>, you should see the file <code>index.html</code> from your repository, and if you visit the domain you set up, you should see your page rendered.</p>
|
||||
<p><a href="TODO.html"><img alt="The static webpage" src="TODO" /></a></p>
|
||||
<h2 id="step-3-using-git-hooks-to-publish-changes">Step 3 — Using Git Hooks to Publish Changes</h2>
|
||||
<p>Now that Nginx is serving the site from our checked out repository, we can set up the <code>post-receive</code> hook to update the site.</p>
|
||||
<h3 id="creating-the-hook-script">Creating the Hook Script</h3>
|
||||
<p>Git hooks for Gitea live in the repository that Gitea serves from. These are located in the data directory, predictably within the <code>repositories</code> subdirectory. Move to your data directory using <code>cd</code>, then move to your repository’s <code>hooks</code> directory:</p>
|
||||
<div class="codehilite"><pre><span></span><code>cd git/repositories/<^>username<^>/static.<^>your_domain<^>.git/hooks
|
||||
</code></pre></div>
|
||||
|
||||
<p>If you list the files in this directory, you will see a whole host of sample scripts, plus a few directories:</p>
|
||||
<div class="codehilite"><pre><span></span><code><span class="k">[secondary_label Output]</span><span class="w"></span>
|
||||
<span class="na">total 92</span><span class="w"></span>
|
||||
<span class="na">drwxr-xr-x 6 git git 4096 Jun 27 17:29 ./</span><span class="w"></span>
|
||||
<span class="na">drwxr-xr-x 7 git git 4096 Jun 27 17:43 ../</span><span class="w"></span>
|
||||
<span class="na">-rwxr-xr-x 1 git git 478 Jun 27 17:29 applypatch-msg.sample*</span><span class="w"></span>
|
||||
<span class="na">-rwxr-xr-x 1 git git 896 Jun 27 17:29 commit-msg.sample*</span><span class="w"></span>
|
||||
<span class="na">-rwxr-xr-x 1 git git 376 Jun 27 17:29 post-receive*</span><span class="w"></span>
|
||||
<span class="na">drwxr-xr-x 2 git git 4096 Jun 27 17:29 post-receive.d/</span><span class="w"></span>
|
||||
<span class="na">-rwxr-xr-x 1 git git 189 Jun 27 17:29 post-update.sample*</span><span class="w"></span>
|
||||
<span class="na">-rwxr-xr-x 1 git git 424 Jun 27 17:29 pre-applypatch.sample*</span><span class="w"></span>
|
||||
<span class="na">-rwxr-xr-x 1 git git 1643 Jun 27 17:29 pre-commit.sample*</span><span class="w"></span>
|
||||
<span class="na">-rwxr-xr-x 1 git git 416 Jun 27 17:29 pre-merge-commit.sample*</span><span class="w"></span>
|
||||
<span class="na">-rwxr-xr-x 1 git git 1374 Jun 27 17:29 pre-push.sample*</span><span class="w"></span>
|
||||
<span class="na">-rwxr-xr-x 1 git git 4898 Jun 27 17:29 pre-rebase.sample*</span><span class="w"></span>
|
||||
<span class="na">-rwxr-xr-x 1 git git 376 Jun 27 17:29 pre-receive*</span><span class="w"></span>
|
||||
<span class="na">drwxr-xr-x 2 git git 4096 Jun 27 17:29 pre-receive.d/</span><span class="w"></span>
|
||||
<span class="na">-rwxr-xr-x 1 git git 544 Jun 27 17:29 pre-receive.sample*</span><span class="w"></span>
|
||||
<span class="na">-rwxr-xr-x 1 git git 1492 Jun 27 17:29 prepare-commit-msg.sample*</span><span class="w"></span>
|
||||
<span class="na">-rwxr-xr-x 1 git git 134 Jun 27 17:29 proc-receive*</span><span class="w"></span>
|
||||
<span class="na">drwxr-xr-x 2 git git 4096 Jun 27 17:29 proc-receive.d/</span><span class="w"></span>
|
||||
<span class="na">-rwxr-xr-x 1 git git 2783 Jun 27 17:29 push-to-checkout.sample*</span><span class="w"></span>
|
||||
<span class="na">-rwxr-xr-x 1 git git 356 Jun 27 17:29 update*</span><span class="w"></span>
|
||||
<span class="na">drwxr-xr-x 2 git git 4096 Jun 27 17:29 update.d/</span><span class="w"></span>
|
||||
<span class="na">-rwxr-xr-x 1 git git 3650 Jun 27 17:29 update.sample*</span><span class="w"></span>
|
||||
</code></pre></div>
|
||||
|
||||
<p>For our purposes, we will be creating a new script within the <code>post-receive.d</code> directory. On Linux, configuration and scripts are usually stored in a directory such as <code>/etc/apt</code> or our <code>hooks</code> directory. However, if there are multiple configuration files or scripts, they may be divided up and placed in a <code>*.d</code> directory, and the service which uses them will read all of those files in list order.</p>
|
||||
<p>Open a new file in <code>post-receive.d</code> for editing named <code>deploy</code>:</p>
|
||||
<div class="codehilite"><pre><span></span><code>nano post-receive.d/deploy
|
||||
</code></pre></div>
|
||||
|
||||
<p>This file can be any executable script. In our case, since we’ll just be running a simple <code>git</code> command, we’ll use a Bash script. Enter the following into that file:</p>
|
||||
<div class="codehilite"><pre><span></span><code><span class="o">[</span>label post-receive.d/deploy<span class="o">]</span>
|
||||
<span class="c1">#!/bin/bash</span>
|
||||
|
||||
<span class="nb">set</span> -eux
|
||||
|
||||
git --git-dir<span class="o">=</span>/var/www/static.<^>your_domain<^>/.git --work-tree<span class="o">=</span>/var/www/static.<^>your_domain<^>/ pull --ff-only
|
||||
</code></pre></div>
|
||||
|
||||
<p>Let’s break this script down into its parts to understand what’s happening:</p>
|
||||
<ul>
|
||||
<li><code>#!/bin/bash</code> tells the shell to use the bash command to run the script.</li>
|
||||
<li><code>set -eux</code> specifies three options: <code>e</code> means that the script will exit on a failure, <code>u</code> means that unset variables will be an error, and <code>x</code> means that it will print the commands as it executes them. These options are good practice for preventing scripts from continuing to execute when there are errors.</li>
|
||||
<li>The <code>git</code> command breaks down into <code>git pull</code> which pulls new changes from the repository, while the <code>--work-tree</code> and <code>--git-dir</code> options tell Git to use the <code>.git</code> directory within the checked out branch for all of its configuration. <code>--ff-only</code> instructs <code>git pull</code> to use fast-forward reconciliation when pulling new commits.</li>
|
||||
</ul>
|
||||
<p>Once you’re done editing the script and have saved and closed it, use <code>chmod</code> to make the script executable:</p>
|
||||
<div class="codehilite"><pre><span></span><code>chmod a+x post-receive.d/deploy
|
||||
</code></pre></div>
|
||||
|
||||
<h3 id="for-gitea-running-in-docker">For Gitea Running in Docker</h3>
|
||||
<p>If Gitea is running in a Docker container — if, for instance, you deployed it using the Docker Compose method in the tutorial linked above — you will need to make a few changes to make the repository accessible from within the container. First, edit the <code>docker-compose.yml</code> file and move to the <code>volumes:</code> entry. Add a new line to that list:</p>
|
||||
<div class="codehilite"><pre><span></span><code><span class="p p-Indicator">[</span><span class="nv">label docker-compose.yml</span><span class="p p-Indicator">]</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="nt">volumes</span><span class="p">:</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">...</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="l l-Scalar l-Scalar-Plain"><^>- /var/www/static.<^>your_domain<^>:/var/www/static.<^>your_domain<^><^></span><span class="w"></span>
|
||||
</code></pre></div>
|
||||
|
||||
<p>This will mount that directory as a volume that the container can access. You will need to restart your Gitea containers:</p>
|
||||
<div class="codehilite"><pre><span></span><code>docker-compose down
|
||||
docker-compose up
|
||||
</code></pre></div>
|
||||
|
||||
<p>Next, you will need to change the remote URL for the repository to the proper location for within the Docker container.</p>
|
||||
<div class="codehilite"><pre><span></span><code>git remote set-url origin file:///data/git/repositories/sammy/static.<^>your_domain<^>.git
|
||||
</code></pre></div>
|
||||
|
||||
<p>Here, we’ve changed the original URL we used to check out the branch from <code>/<^>path_to_gitea_installation<^>/repositories/sammy/static.<^>your_domain<^>.git</code> to use <code>/data/git/</code> as the path to the installation, as that is how the volume is mapped in <code>docker-compose.yml</code>. As always, be sure to use the username and repository name appropriate to your project.</p>
|
||||
<h3 id="testing-the-post-receive-hook">Testing the <code>post-receive</code> Hook</h3>
|
||||
<p>Now, after Gitea receives a new commit and it’s written to disk, it will run this script to update the branch checked out in <code>/var/www/static.<^>your_domain<^></code>. To test this out, change the <code>index.html</code> file on your local working directory to include a congratulations message. For example, change the file so that it resembles this:</p>
|
||||
<div class="codehilite"><pre><span></span><code>[label index.html]
|
||||
<span class="p"><</span><span class="nt">html</span><span class="p">></span>
|
||||
<span class="p"><</span><span class="nt">head</span><span class="p">></span>
|
||||
<span class="p"><</span><span class="nt">title</span><span class="p">></span>Testing Gitea<span class="p"></</span><span class="nt">title</span><span class="p">></span>
|
||||
<span class="p"></</span><span class="nt">head</span><span class="p">></span>
|
||||
<span class="p"><</span><span class="nt">body</span><span class="p">></span>
|
||||
<span class="p"><</span><span class="nt">h1</span><span class="p">></span>It works!<span class="p"></</span><span class="nt">h1</span><span class="p">></span>
|
||||
<span class="p"><</span><span class="nt">p</span><span class="p">></span>Congratulations on setting up a <span class="p"><</span><span class="nt">code</span><span class="p">></span>post-receive<span class="p"></</span><span class="nt">code</span><span class="p">></span> hook!
|
||||
<span class="p"></</span><span class="nt">body</span><span class="p">></span>
|
||||
<span class="p"></</span><span class="nt">html</span><span class="p">></span>
|
||||
</code></pre></div>
|
||||
|
||||
<p>After saving and exiting the file, commit your changes:</p>
|
||||
<div class="codehilite"><pre><span></span><code>git commit -am "Added congratulations message"
|
||||
</code></pre></div>
|
||||
|
||||
<p>And push your changes:</p>
|
||||
<div class="codehilite"><pre><span></span><code>git push origin main
|
||||
</code></pre></div>
|
||||
|
||||
<p>This time, you should see some additional output from Git. Because you set the <code>-x</code> option in our deploy script, you’ll see the output of that script during the process, prefixed with <code>remote:</code>.</p>
|
||||
<div class="codehilite"><pre><span></span><code><span class="k">[secondary_label Output]</span><span class="w"></span>
|
||||
<span class="na">Enumerating objects: 5, done.</span><span class="w"></span>
|
||||
<span class="na">Counting objects: 100% (5/5), done.</span><span class="w"></span>
|
||||
<span class="na">Delta compression using up to 8 threads</span><span class="w"></span>
|
||||
<span class="na">Compressing objects: 100% (2/2), done.</span><span class="w"></span>
|
||||
<span class="na">Writing objects: 100% (3/3), 312 bytes | 312.00 KiB/s, done.</span><span class="w"></span>
|
||||
<span class="na">Total 3 (delta 1), reused 0 (delta 0), pack-reused 0</span><span class="w"></span>
|
||||
<span class="na">remote: + cd /var/www/static.<^>your_domain<^></span><span class="w"></span>
|
||||
<span class="na">remote: + git --git-dir</span><span class="o">=</span><span class="s">/var/www/static.<^>your_domain<^>/.git pull --ff-only</span><span class="w"></span>
|
||||
<span class="na">remote: From file:///data/git/repositories/<^>username<^>/static.<^>your_domain<^></span><span class="w"></span>
|
||||
<span class="na">remote: 28c52bf..95cde6f main -> origin/main</span><span class="w"></span>
|
||||
<span class="na">remote: Updating 28c52bf..95cde6f</span><span class="w"></span>
|
||||
<span class="na">remote: Fast-forward</span><span class="w"></span>
|
||||
<span class="na">remote: . Processing 1 references</span><span class="w"></span>
|
||||
<span class="na">remote: Processed 1 references in total</span><span class="w"></span>
|
||||
<span class="na">To <^>your_domain<^>:<^>username<^>/static.<^>your_domain<^>.git</span><span class="w"></span>
|
||||
<span class="w"> </span><span class="na">8eed10f..95cde6f main -> main</span><span class="w"></span>
|
||||
</code></pre></div>
|
||||
|
||||
<p>When you visit your page in the browser, you should see your now congratulations message displayed.</p>
|
||||
<h2 id="conclusion">Conclusion</h2>
|
||||
<p>In this tutorial, you learned how to use Git hooks in Gitea. While you used the <code>post-receive</code> hook to deploy a static website, there are many available hooks that can help you accomplish a wide variety of tasks during your Git workflow. For more information on these hooks, the Git manual has <a href="https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks">an in-depth chapter</a> on using them.</p>
|
||||
</article>
|
||||
<footer>
|
||||
<p>Page generated on 2022-06-28</p>
|
||||
</footer>
|
||||
</main>
|
||||
<script type="text/javascript">
|
||||
document.querySelectorAll('.tag').forEach(tag => {
|
||||
let text = tag.innerText;
|
||||
tag.innerText = '';
|
||||
tag.innerHTML = `<a href="/tags.html#${text}">${text}</a>`;
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -18,11 +18,16 @@
|
|||
<li><a href="ctm-407.html">How to create temporary and permanent redirects with nginx</a></li>
|
||||
<li><a href="ctm-385.html">How to configure Nginx with SSL as a reverse proxy for Jenkins</a></li>
|
||||
<li><a href="ctm-406.html">How to Access Elements in the DOM</a></li>
|
||||
<li><a href="ctm-514.html">How to Use Struct Tags in Go</a></li>
|
||||
</ul>
|
||||
<h2 id="new">New</h2>
|
||||
<ul>
|
||||
<li><a href="gitea-2.html">Gitea 2</a></li>
|
||||
<li><a href="gitea-3.html">Gitea 3</a></li>
|
||||
</ul>
|
||||
</article>
|
||||
<footer>
|
||||
<p>Page generated on 2022-06-22</p>
|
||||
<p>Page generated on 2022-06-28</p>
|
||||
</footer>
|
||||
</main>
|
||||
<script type="text/javascript">
|
||||
|
|
|
@ -72,7 +72,7 @@
|
|||
<p>“Suddenly we have to prove our greener grass to those phys-side?”</p>
|
||||
<p>“Right. The direction that we need to take with Lagrange can’t just be the same old one we’ve been taking before. In this, True Name and I disagreed.” He shrugged, rocking his glass gently back and forth on the table before taking another sip. “She wanted continue on her path of subtlety, I disagreed.”</p>
|
||||
<p>“Disagreed? You tried to assassinate her, Jonas.”</p>
|
||||
<p>Ey shrugged. “What are bullets but a disagreement?”</p>
|
||||
<p>He shrugged. “What are bullets but a disagreement?”</p>
|
||||
<p>Ioan rolled eir eyes.</p>
|
||||
<p>“We disagree, then. It’s like I said, though, sometimes mommies and daddies fight, Ioan. We’ve spent the past few years trying to hash it out. For all her focus on subtlety in guiding the system, she can be a real bitch when it comes to trying to get her point across.”</p>
|
||||
<p>“Bullshit,” ey said flatly.</p>
|
||||
|
@ -163,7 +163,7 @@
|
|||
<p>“Another time,” ey mumbled, pushing eir face into her soft fur. “I can’t imagine I’m going to be able to sleep tomorrow night, so we might as well get some tonight.”</p>
|
||||
</article>
|
||||
<footer>
|
||||
<p>Page generated on 2022-06-22</p>
|
||||
<p>Page generated on 2022-06-28</p>
|
||||
</footer>
|
||||
</main>
|
||||
<script type="text/javascript">
|
||||
|
|
Loading…
Reference in New Issue