MOVIPREP - polyethylene glycol 3350, sodium sulfate, sodium chloride, potassium chloride, sodium ascorbate and ascorbic acid
Physicians Total Care, Inc.
MoviPrep® is indicated for cleansing of the colon as a preparation for colonoscopy in adults 18 years of age or older.
The MoviPrep dose for colon cleansing for adult patients is 2 liters (approximately 64 ounces) of MoviPrep solution (with 1 additional liter of clear fluids) taken orally prior to the colonoscopy in one of the following ways:
Preparation of the MoviPrep solution:
MoviPrep solution is prepared by emptying the contents of 1 pouch A and 1 pouch B into a suitable glass container (or the container provided) and adding to the container 1 liter of lukewarm water. Mix the solution to ensure that the ingredients are completely dissolved. If the patient prefers, the MoviPrep solution can be refrigerated prior to drinking. The reconstituted solution should be used within 24 hours.
After consumption of the first liter of MoviPrep solution, the above mixing procedure should be repeated with the second pouch A and pouch B to reconstitute the second liter of the MoviPrep solution.
MoviPrep is available in a carton that contains 4 separate pouches (2 of pouch A and 2 of pouch B). Each pouch A contains 100 grams of polyethylene glycol (PEG) 3350, NF, 7.5 grams of sodium sulfate, USP, 2.691 grams of sodium chloride, USP, and 1.015 grams of potassium chloride, USP, plus the following excipients: aspartame, NF (sweetener), acesulfame potassium, NF (sweetener), and lemon flavoring. Each pouch B contains 4.7 grams of ascorbic acid, USP and 5.9 grams of sodium ascorbate, USP.
MoviPrep is contraindicated in patients who have had a severe hypersensitivity reaction to any of its components.
MoviPrep should be used with caution in patients with severe ulcerative colitis, ileus, gastrointestinal obstruction or perforation, gastric retention, toxic colitis, or toxic megacolon
General
Patients with impaired gag reflex and patients prone to regurgitation or aspiration should be observed during the administration of MoviPrep. If a patient experiences severe bloating, abdominal distention, or abdominal pain, administration should be slowed or temporarily discontinued until the symptoms abate. If gastrointestinal obstruction or perforation is suspected, appropriate tests should be performed to rule out these conditions before administration of MoviPrep®.
Phenylketonurics: MoviPrep contains phenylalanine – a maximum of 2.33 mg of phenylalanine per treatment.
No additional ingredients (e.g., flavorings) should be added to the MoviPrep solution.
Since MoviPrep contains sodium ascorbate and ascorbic acid, MoviPrep should be used with caution in patients with glucose-6-phosphate dehydrogenase (G-6-PD) deficiency especially G-6-PD deficiency patients with an active infection, with a history of hemolysis, or taking concomitant medications known to precipitate hemolytic reactions.
5.1 SeizuresThere have been rare reports of generalized tonic-clonic seizures associated with use of polyethylene glycol colon preparation products in patients with no prior history of seizures. The seizure cases were associated with electrolyte abnormalities (e.g., hyponatremia, hypokalemia). The neurologic abnormalities resolved with correction of fluid and electrolyte abnormalities. Therefore, MoviPrep should be used with caution in patients using concomitant medications that increase the risk of electrolyte abnormalities [such as diuretics or angiotensin converting enzyme (ACE)-inhibitors] or in patients with known or suspected hyponatremia. Consider performing baseline and post-colonoscopy laboratory tests (sodium, potassium, calcium, creatinine, and BUN) in these patients.
In the MoviPrep trials, abdominal distension, anal discomfort, thirst, nausea, and abdominal pain were some of the most common adverse reactions to MoviPrep administration. Since diarrhea was considered as a part of the efficacy of MoviPrep, diarrhea was not defined as an adverse reaction in the clinical studies. Tables 1 and 2 display the most common drug-related adverse reactions of MoviPrep and its comparator in the controlled MoviPrep trials.
|
MoviPrep® (split dose) N=180 |
4L PEG + E† N=179 |
|
| n (% = n/N) |
n (% = n/N) |
|
| Malaise |
35 (19.4) |
32 (17.9) |
| Nausea |
26 (14.4) |
36 (20.1) |
| Abdominal pain |
24 (13.3) |
27 (15.1) |
| Vomiting |
14 (7.8) |
23 (12.8) |
| Upper abdominal pain |
10 (5.6) |
11 (6.1) |
| Dyspepsia |
5 (2.8) |
2 (1.1) |
|
MoviPrep® (evening-only) (full dose) N=169 |
90 mL OSPS† N=171 |
|
| n (% = n/N) |
n (% = n/N) |
|
| Abdominal distension |
101 (59.8) |
70 (40.9) |
| Anal discomfort |
87 (51.5) |
89 (52.0) |
| Thirst |
80 (47.3) |
112 (65.5) |
| Nausea |
80 (47.3) |
80 (46.8) |
| Abdominal paint |
66 (39.1) |
55 (32.2) |
| Sleep disorder |
59 (34.9) |
49 (28.7) |
| Rigors |
57 (33.7) |
51 (29.8) |
| Hunger |
51 (30.2) |
121 (70.8) |
| Malaise |
45 (26.6) |
90 (52.6) |
| Vomiting |
12 (7.1) |
14 (8.2) |
| Dizziness |
11 (6.5) |
31 (18.1) |
| Headache |
3 (1.8) |
9 (5.3) |
| Hypokalemia |
0 (0) |
10 (5.8) |
| Hyperphosphatemia |
0 (0) |
10 (5.8) |
Isolated cases of urticaria, rhinorrhea, dermatitis, and anaphylactic reaction have been reported with PEG-based products and may represent allergic reactions.
Published literature contains isolated reports of serious adverse events following the administration of PEG-based products in patients over 60 years of age. These adverse events included upper gastrointestinal bleeding from a Mallory-Weiss tear, esophageal perforation, asystole, and acute pulmonary edema after aspirating the PEG-based preparation.
6.2 Postmarketing ExperienceIn addition to adverse events reported from clinical trials, the following adverse events have been identified during post-approval use of MoviPrep. Because they are reported voluntarily from a population of unknown size, estimates of frequency cannot be made. These events have been chosen for inclusion due to either their seriousness, frequency of reporting or causal connection to MoviPrep, or a combination of these factors.
General: Hypersensitivity reactions including anaphylaxis, rash, urticaria, lip and facial swelling, dyspnea, chest tightness and throat tightness.
Oral medication administered within 1 hour of the start of administration of MoviPrep may be flushed from the gastrointestinal tract and the medication may not be absorbed.
Pregnancy Category C. Animal reproduction studies have not been performed with MoviPrep. It is also not known if MoviPrep can cause fetal harm when administered to a pregnant woman or can affect reproductive capacity. MoviPrep should be given to a pregnant woman only if clearly needed.
8.3 Nursing MothersBecause many drugs are excreted in human milk, caution should be exercised when MoviPrep is administered to a nursing woman.
8.4 Pediatric UseThe safety and effectiveness of MoviPrep in pediatric patients
has not been established.
Of the 413 patients in clinical studies receiving MoviPrep, 91 (22%) patients were aged 65 or older, while 25 (6%) patients were over 75 years of age. No overall differences in safety or effectiveness were observed between geriatric patients and younger patients, and other reported clinical experience has not identified differences in responses between geriatric patients and younger patients, but greater sensitivity of some older individuals cannot be ruled out.
There have been no reported cases of overdose with MoviPrep. Purposeful or gross accidental ingestion of more than the recommended dose of MoviPrep might be expected to lead to severe electrolyte disturbances, including hyponatremia and/or hypokalemia, as well as dehydration and hypovolemia, with signs and symptoms of these disturbances. The patient who has taken an overdose should be monitored carefully, and treated symptomatically for complications until stable.
MoviPrep® consists of 4 separate pouches (2 of pouch A and 2 of pouch B)
containing white to yellow powder for reconstitution. Each pouch A contains 100
grams of polyethylene glycol (PEG) 3350, NF, 7.5 grams of sodium sulfate, USP,
2.691 grams of sodium chloride, USP, and 1.015 grams of potassium chloride, USP,
plus the following excipients: aspartame, NF (sweetener), acesulfame potassium,
NF (sweetener), and lemon flavoring. Each pouch B contains 4.7 grams of ascorbic
acid, USP and 5.9 grams of sodium ascorbate, USP. When 1 pouch A and 1 pouch B
are dissolved together in water to a volume of 1 liter, MoviPrep (PEG-3350,
sodium sulfate, sodium chloride, potassium chloride, sodium ascorbate, and
ascorbic acid) is an oral solution having a lemon taste.
The entire,
reconstituted, 2-liter MoviPrep colon preparation contains 200 grams of
PEG-3350, 15 grams of sodium sulfate, 5.38 grams of sodium chloride, 2.03 grams
of potassium chloride, 9.4 grams of ascorbic acid, and 11.8 grams of sodium
ascorbate plus the following excipients: aspartame (sweetener), acesulfame
potassium (sweetener), and lemon flavoring.
MoviPrep produces a watery stool leading to cleansing of the colon. The osmotic activity of polyethylene glycol 3350, sodium sulfate, sodium chloride, potassium chloride, sodium ascorbate, and ascorbic acid, when taken with 1 liter of additional clear fluid, usually results in no net absorption or excretion of ions or water.
The pharmacokinetics of MoviPrep have not been studied in patients with renal or hepatic insufficiency.
Long-term studies in animals to evaluate the carcinogenic potential have not been performed with MoviPrep®. Studies to evaluate potential for impairment of fertility or mutagenic potential have not been performed with MoviPrep®.
The colon cleansing efficacy and safety of MoviPrep was evaluated in two randomized, actively-controlled, multi-center, investigator-blinded, phase 3 trials in patients scheduled to have an elective colonoscopy.
In the first study, patients were randomized to one of the following two colon preparation treatments: 1) 2 liters of MoviPrep with 1 additional liter of clear fluid split into two doses (during the evening before and the morning of the colonoscopy) and 2) 4 liters of polyethylene glycol plus electrolytes solution (4L PEG + E) split into two doses (during the evening before and the morning of the colonoscopy). Patients were allowed to have a morning breakfast, a light lunch, clear soup and/or plain yogurt for dinner. Dinner had to be completed at least one hour prior to initiation of the colon preparation administration.
The primary efficacy endpoint was the proportion of patients with effective
colon cleansing as judged by blinded gastroenterologists on the basis of
videotapes recorded during the colonoscopy.
The blinded gastroenterologists
graded the colon cleansing twice (during introduction and withdrawal of the
colonoscope) and the poorer of the two assessments was used in the primary
efficacy analysis.
The efficacy analysis included 308 adult patients who had an elective colonoscopy. Patients ranged in age from 18 to 88 years old (mean age about 59 years old) with 52% female and 48% male patients. Table 3 displays the results.
|
Responders A* or B† (%) |
C‡ (%) |
D§ (%) | |
|
MoviPrep® (N=153) |
88.9 |
9.8 |
1.3 |
|
4L PEG + E¶ (N=155) |
94.8 |
4.5 |
0.6 |
In the second study, patients were randomized to one of the following two
colon preparation treatments: 1) 2 liters of MoviPrep with 1 additional liter
of clear fluid in the evening prior to the colonoscopy and 2) 90 mL of oral
sodium phosphate solution (90 mL OSPS) with at least 2 liters of additional
clear fluid during the day and evening prior to the colonoscopy. Patients
randomized to MoviPrep therapy were allowed to have a morning breakfast; a light
lunch; and clear soup and/or plain yogurt for dinner. Dinner had to be
completed at least one hour prior to initiation of the colon preparation
administration.
The primary efficacy endpoint was the proportion of
patients with effective colon cleansing as judged by the colonoscopist and one
blinded gastroenterologist (on the basis of videotapes recorded during the
colonoscopy). In case of a discrepancy between the colonoscopist and the
blinded gastroenterologist, a second blinded gastroenterologist made the final
efficacy determination.
|
|
Responders A* or B† (%) |
C‡ (%) | D§ (%) |
|
MoviPrep® (N=137) |
73.0 |
23.4 |
3.6 |
|
90 mL OSPS¶ (N=143) |
64.4 |
29.4 |
6.3 |
MoviPrep® is supplied in powdered form. MoviPrep is administered as an oral solution after reconstitution.
NDC 54868-5890-0, MoviPrep, single use carton.
Each carton contains a disposable container for reconstitution of MoviPrep® and an inner carton containing 4 pouches (2 of pouch A and 2 of pouch B).
Rx only
STORAGE
Store
carton/container at 25°C (77°F); excursions permitted to 15-30°C (59-86°F). When
reconstituted, store upright and keep solution refrigerated. Use within 24
hours.
Important Precautions Regarding MOVIPREP
MoviPrep produces a watery stool which cleanses the colon before colonoscopy. It is recommended that patients receiving MoviPrep be advised to adequately hydrate before, during, and after the use of MoviPrep. Patients may have clear soup and/or plain yogurt for dinner, finishing the evening meal at least one hour prior to the start of MoviPrep treatment. No solid food should be taken from the start of MoviPrep treatment until after the colonoscopy.
What Patients Should Know About Adverse Reactions
The first bowel movement may occur approximately 1 hour after the start of MoviPrep administration. Abdominal bloating and distention may occur before the first bowel movement. If severe abdominal discomfort or distention occurs, stop drinking temporarily or drink each portion at longer intervals until these symptoms disappear.
Manufactured by:
Norgine
B.V.
Hogehilweg 7
1101 CA Amsterdam Zuidoost
Netherlands
For:
Salix Pharmaceuticals,
Inc.
Morrisville, NC 27560
© 2006 Salix Pharmaceuticals, Inc.
VENART xxx-x/June 09
Product protected by U.S. Patent No. 7169381 and other pending applications
MoviPrep®
(PEG-3350, Sodium
Sulfate, Sodium Chloride, Potassium Chloride, Sodium Ascorbate and Ascorbic Acid
for Oral Solution)
Phenylketonurics:
contains phenylalanine – a maximum of 2.33 mg per course
of treatment.
This carton contains
One container for reconstitution of MoviPrep®
|
MOVIPREP
polyethylene glycol 3350, sodium sulfate, sodium chloride, potassium chloride, sodium ascorbate and ascorbic acid kit | ||||||||||||||||||
| ||||||||||||||||||
| ||||||||||||||||||
| ||||||||||||||||||
| ||||||||||||||||||
| ||||||||||||||||||
| ||||||||||||||||||
| ||||||||||||||||||
| ||||||||||||||||||
| ||||||||||||||||||
| ||||||||||||||||||
| ||||||||||||||||||
| ||||||||||||||||||
| ||||||||||||||||||
| ||||||||||||||||||
| ||||||||||||||||||
|
| Labeler - Physicians Total Care, Inc. (194123980) |
| Establishment | |||
| Name | Address | ID/FEI | Business Operations |
|---|---|---|---|
| Physicians Total Care, Inc. | 194123980 | relabel |


|
||||
|
Easy steps to get visual statistics using statistics generator : Step 1 -> Register as a new user (If not registered previously) Step 2 -> Login Step 3 -> Upload your excel file Step 4 -> Select the appropriate field for visual statistics Step 5 -> Get a visual statistic |
||||
|
|
||||
| © Project StatGen 2011 |


| ID | Title | Due Date | Priority | Status | Action |
|---|---|---|---|---|---|
| 2 | Wash the car | 02/02/2014 | MEDIUM | DONE | Edit Delete |
| 9 | Change password | 30/04/2014 | MEDIUM | TODO | Edit Delete |
| 1 | Prepare birthday party | 25/05/2014 | MEDIUM | DONE | Edit Delete |
| 4 | Buy a gift for mom | 25/07/2014 | HIGH | DONE | Edit Delete |
| 5 | Remember the milk | 12/08/2014 | LOW | TODO | Edit Delete |
| 6 | Prepare for interview | 12/08/2014 | LOW | DONE | Edit Delete |
| 10 | Send weekly reports | 22/08/2014 | HIGH | TODO | Edit Delete |
| 7 | Email David about project | 10/10/2014 | HIGH | DONE | Edit Delete |
| 3 | Take out the trash | 11/10/2014 | HIGH | TODO | Edit Delete |
| 8 | Feed the dog | 12/12/2014 | LOW | TODO | Edit Delete |
|
Total = 10
|
Todo = 5
|
Done = 5
|


Armed Robbery at Chase Palm Park 23 Nov 2019, 13:25 by Roger Code 211: Armed robbery near 236 East Cabrillo at Chase Palm Park.


Cookie Stand
Home
Please fill in the information below.
New Store Locations:
Location:
Minimum Cookies Sold
Max Cookies Sold
Avg Cookies Per Hour


Home
Game overview
How to play
About us
Contact us
Options
Main Menu
Theme
Sound
On
Off
Difficulty
Easy
Hard


jadx
Dex to Java decompiler.
Produces Java source code from Android Dex and APK files.
More information: home page
- Decompile a Dex file into a directory:
jadx path/to/file
- Decompile a Dex file into a specific directory:
jadx --output-dir path/to/directory path/to/file


Explain why this "something" is valuable to customers. Keep the language simple and try to get to the point. More than three lines of conveniently readable text are probably overkill.
Learn MoreThe network icon would be appropriately used here if networking represent a core aspect that is addressed by our solution offering.
More about NetworkingThese blocks are meant to discuss features or benefits that users would be able to leverage by using the provided service or product.
Explore moreIf we offered something by way of robotic solutions or cybernation tooling, we would talk about its benefits in this section.
Explore cybernationIf we offered something by way of robotic solutions or cybernation tooling, we would talk about its benefits in this section.
More about SecurityOffering some well-defined benefit for certain roles, stages, or processes such that everyone can rest easier.
If the concept of a stack is relevant to your particular case, please go ahead and provide a summary of why addressing the individual components or abstraction levels is relevant.
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
This is where one describes the one-liner in more detail. If the one-liner is about integrations, talk about why those integrations are supported.
Explore all integrationsRequest a demo or talk to a solutions consultant to explore options to address your problems.


A brief analysis of global weather data
As expected, temperatures tend to correlate depending on the relative distance from the equator. But is distance really the biggest factor?
For the purpose of this analysis, I wanted to study other factors such as cloud coverage, humidity, temperature, and wind speed to gain a better understanding of what impacts weather patterns and how those correlate with latitude and temperature.
Check out the Charts »


Mitigation
The best way to mitigate this bypass is just by not giving users local admin rights to their machines. Majority of user accounts in a corporate environment you should be able to do this reducing the attack surface. This however does not apply home users which would have local admin rights by default.
The actual bypass only works when set to the middle two UAC settings which will let it auto-elevate.
Notify me only when apps try to make changes to my computer (default)
Notify me only when apps try to make changes to my computer (do not dim desktop settings)
To see your settings you need to go to Control Panel – User Accounts – Change User Account Control settings.
so we could set to Always notify but this would bring it back to like it was on Windows Vista with constant notifications and not really practical and the user would end up setting it to Never notify which is definitely not a good idea.
Microsoft has given us 10 UAC policies to play with so it’s worth spending some time understanding and testing these out before implementing it in your own domain environment. To see what is applied on your local machine type secpol.msc into Start-Run to open the Local Security Policy snap-in and expand the Local Policies-Security Options folder. Run rsop.msc to view group policies applied on machines in a domain environment.
Looking in the registry these are the default values of UAC
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System]
"ConsentPromptBehaviorAdmin"=dword:00000005
"ConsentPromptBehaviorUser"=dword:00000003
"EnableInstallerDetection"=dword:00000001
"EnableLUA"=dword:00000001
"EnableSecureUIAPaths"=dword:00000001
"EnableUIADesktopToggle"=dword:00000000
"EnableVirtualization"=dword:00000001
"FilterAdministratorToken"=dword:00000000
"PromptOnSecureDesktop"=dword:00000001
"ValidateAdminCodeSignatures"=dword:00000000
When the slider is moved upto “Always notify me” it changes this value
"ConsentPromptBehaviorAdmin"=dword:00000002
When the slider is moved down to “Notify me only when apps try to make changes to my computer (do not dim desktop settings)” it changes this value
"PromptOnSecureDesktop"=dword:00000000
And when the slider is moved to “Never notify” the values changed are
"ConsentPromptBehaviorAdmin"=dword:00000000
"EnableLUA"=dword:00000000
"PromptOnSecureDesktop"=dword:00000000
Take note that EnableLUA has been disabled completely. This is an extremely dangerous value to be in and should never be disabled so its strongly recommend to set this settings to be enabled in group policies so it always gets applied if settings are reset/changed by users or by previously removed malware.
User Account Control: Run all administrators in Admin Approval Mode
Once disabled not only a malicious process could be able to go straight to high integrity without any bypass but also Internet Explorer would run in medium integrity. UAC gives us the Protected Mode (sandbox) in Internet Explorer providing added security. Internet Explorer normally runs in low integrity child process so if compromised by some IE exploit the damage is minimized as in low integrity there are only a handful of locations it can be written to on the system.
These changes mentioned above have been seen on Windows 7. On Windows 8/8.1 EnableLUA does not change to disabled. So when the slider is moved to Never notify the values changed are only
"ConsentPromptBehaviorAdmin"=dword:00000000
"PromptOnSecureDesktop"=dword:00000000
Since value “EnableLUA”=dword:00000001 does not change, UAC is not completely disabled and Internet Explorer would still run in low integrity.
If however a user logged onto a machine using the local admin account (administrator or whatever renamed on your corporate build) UAC settings does not apply as all processes run in high integrity. This applies to Windows 7/8 and 8.1 so always make sure users DO NOT logon using local admin account, if local admin rights are required better add their domain account to the local administrators group.
If for whatever reason logging on using the local admin account is a necessity then best set this UAC policy to enabled.
User Account Control: Admin Approval Mode for the built-in Administrator account
“FilterAdministratorToken”=dword:00000001
Another option would be to look into renaming or deleting the executables Mcx2Prov.exe, sysprep.exe, cliconfg.exe and pwcreator.exe if definitely not required on the system so the second stage to exploit DLL hijacking fails.
Finally if users do require local admin privileges then worth setting their machine UAC policy to Always notify and they live with the constant notifications.
User Account Control: Behavior of the elevation prompt for administrators in Admin Approval Mode (2-Prompt for consent on the secure desktop)
Conclusion
This bypass only works when all of the requirements are available to abuse. Remove one requirement and the bypass will fail. Office documents are opened in medium integrity so these are ideal targets to abuse the UAC bypass. Since these bypasses are so effortlessly achieved the only real course of action would be to set UAC to “Always notify” or remove local admin rights for the user. In the end using agents like Microsoft EMET or MalwareBytes Anti-Exploit would be the best mitigating action to take from initially being exploited in the first place.
Here are the source and binaries you can test for yourself. I tested it on Windows Enterprise 7/8/8.1 64bit


Vaadin Kotlin PWA
You are offline
Vaadin Kotlin PWA Demo requires an internet connection to work. You do not seem to have access to the server right now. Check your internet connection and try reloading the page to use the application.


JINN augments the skill set of dedicated founders and teams with product, technical, and business expertise. Through their unique Design, Launch, and Grow process JINN transforms ideas into scalable products that fulfill a clear market need while meeting your business objectives in the long run.
JINN’s culture surrounds the success of your business. They act as the guiding walls to control the variability that arises when building and growing a business. You can think of JINN as an extension of your team.
Intelligent Payment Networks' goal is to ensure that our clients have a better understanding of how payments are processed, thus making better decisions regarding what type of payment processing system that best suits their business needs, in so that they increase revenue, reduce costs, and streamline transaction processing by utilizing "intelligent" technologies, products and services.


WYSIWYG
`Prev `__
W
`Next <../X.html>`__
--------------
**WYSIWYG**: /wiz´ee·wig/, /wiss´ee·wig/, adj.
[Traced to Flip Wilson's “Geraldine” character c.1970] Describes a user
interface under which “What You See Is What You Get”, as opposed to one
that uses more-or-less obscure commands that do not result in immediate
visual feedback.  All these can be mildly derogatory, as they are often used to
refer to dumbed-down `*user-friendly* <../U/user-friendly.html>`__
interfaces targeted at non-programmers; a hacker has no fear of obscure
commands (compare `*WYSIAYG* `__). On the other hand,
`*EMACS* <../E/EMACS.html>`__ was one of the very first WYSIWYG editors,
replacing (actually, at first overlaying) the extremely obscure,
command-based `*TECO* <../T/TECO.html>`__. See also `*WIMP
environment* `__. [Oddly enough, WYSIWYG made it
into the 1986 supplement to the OED, in lower case yet. —ESR]
--------------
+----------------------------+----------------------------+-------------------------+
| `Prev `__ | `Up <../W.html>`__ | `Next <../X.html>`__ |
+----------------------------+----------------------------+-------------------------+
| WYSIAYG | `Home <../index.html>`__ | X |
+----------------------------+----------------------------+-------------------------+


Support for transactions (rollback, commit, etc).
Automates transactions and interaction between object graph and datasources.


| | |
- builtins.object
-
- Character
class Character(builtins.object) |
| |
Character(name, position, inventory)
A class that models an character object |
| |
Methods defined here:
- __init__(self, name, position, inventory)
- Initialize self. See help(type(self)) for accurate signature.
- add_item(self, item_name)
- This function adds an item to the character.
Parameters:
item_name (dict): A dictionary of possible items.
Returns:
A string verifying that he player has the item.
- delete_item(self, item_name)
- This function removes an item from the character
Parameters:
item_name (dict): A dictionary of possible items
Returns:
A string indicating the state of the item.
- has_item(self, item_name)
- This function determines if the player is holding an item.
Parameters:
item_name (dict): A dictionary of possible items
Returns:
item_name (bool): True if the player holds the item,
false otherwise.
Data descriptors defined here:
- __dict__
- dictionary for instance variables (if defined)
- __weakref__
- list of weak references to the object (if defined)
| |


--- layout: post title: "Typing is not a programming bottleneck" description: "Some developers seem to think that typing is a major bottleneck while programming. It's not." date: 2018-09-17 8:16 UTC tags: [Productivity] --- {% include JB/setup %}
{{ page.description }}
I sometimes give programming advice to people. They approach me with a software design problem, and, to the best of my ability, I suggest a remedy. Despite my best intentions, my suggestions sometimes meet resistance. One common reaction is that my suggestion isn't idiomatic, but recently, another type of criticism seems to be on the rise.
The code that I suggest is too verbose. It involves too much typing.
I'll use this article to reflect on that criticism.
Before we get into the details of the issue, I'd like to start with the big picture. What's the purpose of code?
I've discussed this extensively in my Clean Coders video on Humane Code. In short, the purpose of source code is to communicate the workings of a piece of software to the next programmer who comes along. This could easily include your future self.
Perhaps you disagree with that proposition. Perhaps you think that the purpose of source code is to produce working software. It's that, too, but that's not its only purpose. If that was the only purpose, you might as well write the software in machine code.
Why do we have high-level programming languages like C#, Java, JavaScript, Ruby, Python, F#, Visual Basic, Haskell, heck - even C++?
As far as I can tell, it's because programmers are all human, and humans have limited cognitive capacity. We can't keep track of hundreds, or thousands, of global variables, or the states of dozens of complex resources. We need tools that help us structure and understand complex systems so that they're broken down into (more) manageable chunks. High-level languages help us do that.
The purpose of source code is to be understood. You read source code much more that you write. I'm not aware of any scientific studies, but I think most programmers will agree that over the lifetime of a code base, any line of code will be read orders of magnitude more often that it's edited.
How many lines of code do you produce during a productive working day?
To be honest, I can't even answer that question myself. I've never measured it, since I consider it to be irrelevant. The Mythical Man-Month gives the number as 10, but let's be generous and pretend it's ten times that. This clearly depends on lots of factors, such as the language in which you're writing, the state of the code base, and so on. You'd tend to write more lines in a pristine greenfield code base, whereas you'll write fewer lines of code in a complicated legacy code base.
How many characters is a line of code? Let's say it's 80 characters, because that's the maximum width code ought to have. I realise that many people write wider lines, but on the other hand, most developers (fortunately) use indentation, so as a counter-weight, code often has some blank space to the left as well. This is all back-of-the-envelope calculations anyway.
When I worked in a Microsoft product group, we typically planned that a productive, 'full' day of coding was five hours. Even on productive days, the rest was used in meetings, administration, breaks, and so on.
If you write code for five hours, and produce 100 lines of code, at 80 characters per line, that's 8,000 characters. Your IDE is likely to help you with statement completion and such, but for the sake of argument, let's pretend that you have to type it all in.
8,000 characters in five hours is 1,600 characters per hour, or 27 characters per minute.
I'm not a particularly fast typist, but I can type ten times faster than that.
Typing isn't a bottleneck.
I tend to get drawn into these discussions from time to time, but good programming has little to do with how fast you can produce lines of code.
To be clear, I entirely accept that statement completion, refactoring support, and such are, in general, benign features. While I spend most of my programming time thinking and reading, I also tend to write code in bursts. The average count of lines per hour may not be great, but that's because averages smooth out the hills and the valleys of my activity.
Still, increasingly frequent objection to some of my suggestions is that what I suggest implies 'too much' code. Recently, for example, I had to defend the merits of the Fluent Builder pattern that I suggest in my DI-Friendly Library article.
As another example, consider two alternatives for modelling a restaurant reservation. First, here's a terse option:
public class Reservation { public DateTimeOffset Date { get; set; } public string Email { get; set; } public string Name { get; set; } public int Quantity { get; set; } public bool IsAccepted { get; set; } }
Here's a longer alternative:
public sealed class Reservation { public Reservation( DateTimeOffset date, string name, string email, int quantity) : this(date, name, email, quantity, false) { } private Reservation( DateTimeOffset date, string name, string email, int quantity, bool isAccepted) { Date = date; Name = name; Email = email; Quantity = quantity; IsAccepted = isAccepted; } public DateTimeOffset Date { get; } public string Name { get; } public string Email { get; } public int Quantity { get; } public bool IsAccepted { get; } public Reservation WithDate(DateTimeOffset newDate) { return new Reservation(newDate, Name, Email, Quantity, IsAccepted); } public Reservation WithName(string newName) { return new Reservation(Date, newName, Email, Quantity, IsAccepted); } public Reservation WithEmail(string newEmail) { return new Reservation(Date, Name, newEmail, Quantity, IsAccepted); } public Reservation WithQuantity(int newQuantity) { return new Reservation(Date, Name, Email, newQuantity, IsAccepted); } public Reservation Accept() { return new Reservation(Date, Name, Email, Quantity, true); } public override bool Equals(object obj) { if (!(obj is Reservation other)) return false; return Equals(Date, other.Date) && Equals(Name, other.Name) && Equals(Email, other.Email) && Equals(Quantity, other.Quantity) && Equals(IsAccepted, other.IsAccepted); } public override int GetHashCode() { return Date.GetHashCode() ^ Name.GetHashCode() ^ Email.GetHashCode() ^ Quantity.GetHashCode() ^ IsAccepted.GetHashCode(); } }
Which alternative is better? The short version is eight lines of code, including the curly brackets. The longer version is 78 lines of code. That's ten times as much.
I prefer the longer version. While it takes longer to type, it comes with several benefits. The main benefit is that because it's immutable, it can have structural equality. This makes it trivial to compare objects, which, for example, is something you do all the time in unit test assertions. Another benefit is that such Value Objects make better domain models. The above Reservation
Value Object only shows the slightest sign of emerging domain logic in the Accept
method, but once you start modelling like this, such objects seem to attract more domain behaviour.
Perhaps you're now convinced that typing speed may not be the bottleneck, but you still feel that you don't like the verbose Reservation
alternative. More code could be an increased maintenance burden.
Consider those With[...]
methods, such as WithName
, WithQuantity
, and so on. Once you make objects immutable, such copy-and-update methods become indispensable. They enable you to change a single property of an object, while keeping all other values intact:
> var r = new Reservation(DateTimeOffset.Now, "Foo"<EMAIL_ADDRESS>3); > r.WithQuantity(4) Reservation { Date=[11.09.2018 19:19:29 +02:00]<EMAIL_ADDRESS>IsAccepted=false, Name="Foo", Quantity=4 }
While convenient, such methods can increase the maintenance burden. If you realise that you need to change the name of one of the properties, you'll have to remember to also change the name of the copy-and-update method. For example, if you change Quantity
to NumberOfGuests
, you'll have to also remember to rename WithQuantity
to WithNumberOfGuests
.
I'm not sure that I'm ready to concede that this is a prohibitive strain on the sustainability of a code base, but I do grant that it's a nuisance. This is one of the many reasons that I prefer to use programming languages better equipped for such domain modelling. In F#, for example, a record type similar to the above immutable Reservation
class would be a one-liner:
type Reservation =
{ Date : DateTimeOffset; Name : string; Email : string; Quantity : int; IsAccepted : bool }
Such a declarative approach to types produces an immutable record with the same capabilities as the 78 lines of C# code.
That's a different story, though. There's little correlation between the size of code, and how 'good' it is. Sometimes, less code is better; sometimes, more code is better.
I'll dispense with the usual Edsger Dijkstra and Bill Gates quotes on lines of code. The point that lines of code is a useless metric in software development has been made already. My point is a corollary. Apparently, it has to be explicitly stated: Programmer productivity has nothing to do with typing speed.
Unless you're disabled in some way, you can type fast enough to be a productive programmer. Typing isn't a programming bottleneck.
Comments
Watch a bad typist code. (It's easier with coding-heavy tutorial videos). It usually go as: type four or five characters, backspace, make a correction, type another six characters, backspace, make another fix, type a few more character, etc.
They rarely get more than 10 keystrokes out before have to stop and go back. This reeks havoc with your cognitive flow, and makes it much harder to get into the "groove" of coding. Once you can type fluidly, you can concetrate on the code itself, rather than the mechanics of entering it.
Moreover, in many cases, too much verbosity might only lead to more defects (unless you're a strong advocate of TDD et similia)
That said, one thing that I really liked about your article is the version of the Builder pattern you've implemented in the Reservation class. I love immutability in my classes and I often use the Builder pattern implemented as a inner class, just to be able to access the private cTor.
One thing that I don't like with your approach is that in case you have a bit parameter list in your cTor (as you do in the example), the consumers are forced to create a "dummy" instance and then call the WithXXX() methods on it when possible. I guess it could be solved adding a static readonly Empty property on the Reservation class that can be used as a starting point.
Anyways thank you for sharing this!
James, thank you for writing. I agree that being able to type out your thoughts as fast as possible lowers the barrier between your brain and whatever medium you're working in. I had something similar in mind when I wrote
I do, however, have mounting concerns about what you call the groove of coding. Flow state is generally characterised by a lack of reflection that I have often found to be counter-productive. These days, when programming, I deliberately use the Pomodoro technique - not to motivate me to code, but to force me to take breaks. Uncountable are the times I've had an important realisation about my work as soon as I'm away from the keyboard. These insights never seem to come to me when I'm in flow.You could create a more generic
With
method that would enable modifying any property. Here is an alternative design of theReservation
class including such a method. It is untested, so it might not work properly in every case.Of course this design is not type-safe and we are throwing exceptions whenever the types of property and value don't match. Property names can be passed using
nameof
which will provide compile-time feedback. I believe it would be possible to write a code analyzer that would help calling theWith
method by raising compilation errors whenever the types don't match. TheWith
method could be even designed as an extension method onobject
and distributed in a Nuget package with the anayler alongside it.


Hello Dev!
With this you have many features:
- HTML5 Boilerplate
- Swig Template Engine, for smart views
- NPM as a package manager
- Sass (with libsass for higher performance)
- ES6/JSX Ready and ESLint
- Test Suite with Karma, Mocha, Expect and Sinon
- Autoprefixer
- Browser Sync (live browser reloading and cross-device synchronization)
- jQuery and Normalize as default
- Bourbon and Neat to provide some mixins and a Grid System
- HTML minification
- Assets are concatenated and compressed (JS, CSS and images)


Washington D.C. Area Photographer
We are a boutique photography studio specializing in creative and inspired wedding, engagement and editorial photography. Our team has over a decade of experience in the publishing, fashion, beauty and wedding industries. Blending classic and contemporary styles, we will help you immortalize your most precious and joyous occasions.
Proudly serving Washington D.C., Maryland and Virginia


Match Play. Red trails 2-4 to 7. Should Red double? If doubled, should White take?
pips Red=144 White=155
(CorrectAnswer= 0/0, ErrorScore= 0)
Yet another position where the match score has a huge influence.
For money this is a double but it also a trivial take. At 2-4 to 6 a gammon takes Red to the Crawford Game and so the warning lights should flash for White. So much so that is a clear blunder to take.
Hopefully if I post enough positions like this one I will get my message across!!
For money this is a double but it also a trivial take. At 2-4 to 6 a gammon takes Red to the Crawford Game and so the warning lights should flash for White. So much so that is a clear blunder to take.
Hopefully if I post enough positions like this one I will get my message across!!
1. Cubeful 2-ply 9/2* Eq.: +1.376 0.828 0.360 0.015 - 0.172 0.019 0.000 2. Cubeful 2-ply 8/2* 6/5 Eq.: +1.369 (-0.007) 0.816 0.368 0.015 - 0.184 0.028 0.001 3. Cubeful 2-ply 8/2*/1 Eq.: +1.354 (-0.022) 0.811 0.366 0.015 - 0.189 0.029 0.001 4. Cubeful 2-ply 18/17 8/2* Eq.: +1.338 (-0.038) 0.813 0.356 0.016 - 0.187 0.029 0.001 5. Cubeful 2-ply 18/11 Eq.: +1.293 (-0.083) 0.841 0.305 0.011 - 0.159 0.018 0.000


CHAPTER 95
DESTROYING OR DEFACING OFFICIAL NOTICES
REPEALED. L 1991, c 223, §3.
Cross References
For present provision, see §710-1019.


vanna's
vanna's
We promise you a new look and more importantly, a new ambiance. We can achieve that by getting to know you, your needs and creating the best looking and healthy plants.
We promise you a new look and more importantly, a new ambiance. We build that by getting to know you, your needs and creating the best looking and healthy plants.
Product Owner
I've been with my plants since I was starting living here in the province. It became my hobby and few months happened,they're all grown and at the same time healthy with giving me a lot of seedlings. Now I'll be sharing with you my plants which made me happy in life
Product Seller
Been watching her-Mrs. Marishel took care and let it live as time past by. I became a watcher of hers and been thinking a best way to appoarch the world for giving a better ambiance at their homes.
Product Deliverer
I'll be helping on giving the customers best plant to achieve the ambiance you' ve wanted.
We are keen on creating an ambiance for anyone with a sense of nature! We design our homes with our best plants that will be posting soon. And having our customers in mind and we never disappoint!


{% extends "sidebar-page.html" %} {% block content %}
{% autoescape off%}{{n.summary}}{% endautoescape %} Read More...
Can't find what you're looking for? Check our news archive


The restaurant is an organic space reflective of nature inspired cuisine.The interplay of textures and colour brings life and a vibrance that embraces therestaurant’s place in the dress circle of Jorhat. An ode to the assam'slandscape, from the vast nature floor, to the cracked bark of a paperbark tree, every detail from the ground up has been thoughtfully considere


Working with Data¶
SeisIO is designed around the principle of easy, fluid, and fast data access. At the most basic level, SeisIO uses an array-like custom structure called a SeisChannel for single-channel data; SeisData structures store multichannel data and can be created by combining SeisChannel objects.
First Steps¶
Create a new, empty SeisChannel object with
-
Ch = SeisChannel()
The meanings of the field names are explained here<dkw>. You can edit field values manually, e.g.,
Ch.loc = [-90.0, 0.0, 9300.0, 0.0, 0.0]
Ch.name = "South pole"
or you can set them with keywords at creation:
Ch = SeisChannel(name="MANOWAR JAJAJA")
SeisData structures are collections of channel data. They can be created with the SeisData() command, which can optionally create any number of empty channels at a time, e.g.,
-
S = SeisData(1)
They can be explored similarly:
S.name[1] = "South pole"
S.loc[1] = [-90.0, 0.0, 9300.0, 0.0, 0.0]
A collection of channels becomes a SeisData structure:
-
S = SeisData(SeisChannel(), SeisChannel())
You can push channels onto existing SeisData structures, like adding one key to a dictionary:
-
push!(S, Ch)
Note that this copies Ch to a new channel in S – S[3] is not a view into C.
This is deliberate, as otherwise the workspace quickly becomes a mess of
redundant channels. Clean up with Ch = []
to free memory before moving on.
Operations on SeisData structures¶
We’re now ready for a short tutorial of what we can do with data structures. In the commands below, as in most of this documentation, Ch is a SeisChannel object and S is a SeisData object.
Adding channels to a SeisData structure¶
You’ve already seen one way to add a channel to SeisData: push!(S, SeisChannel()) adds an empty channel. Here are others:
-
append!(S, SeisData(n))
Adds n channels to the end of S by creating a new n-channel SeisData and appending it, similar to adding two dictionaries together.
These methods are aliased to the addition operator:
S += SeisChannel() # equivalent to push!(S, SeisChannel())
S += randseisdata(3) # adds a random 3-element SeisData structure to S in place
S = SeisData(randseisdata(5), SeisChannel(),
SeisChannel(id="UW.SEP..EHZ", name="Darth Exploded",
loc=[46.1967, -122.1875, 1440, 0.0, 0.0]))
Most web request functions can append to an existing SeisData object by placing an exclamation mark after the function call. You can see how this works by running the examples<webex>.
Search, Sort, and Prune¶
The easiest way to find channels of interest in a data structure is to use findid, but you can obtain an array of partial matches with findchan:
S = SeisData(randseisdata(5), SeisChannel(),
SeisChannel(id="UW.SEP..EHZ", name="Darth Exploded",
loc=[46.1967, -122.1875, 1440, 0.0, 0.0], x=rand(1024)))
findid(S, "UW.SEP..EHZ") # 7
findchan(S, "EHZ") # [7], maybe others depending on randseisdata
You can sort by channel ID with the sort command.
Several functions exist to prune empty and unwanted channels from SeisData structures.
delete!(S, 1:2) # Delete first two channels of S
S -= 3 # Delete third channel of S
# Extract S[1] as a SeisChannel, removing it from S
C = pull(S, 1)
# Delete all channels whose S.x is empty
prune!(S)
# Delete channels containing ".SEP."
delete!(S, ".SEP.", exact=false)
In the last example, specifying exact=false means that any channel whose ID partly matches the string “.SEP.” gets deleted; by default, passing a string to delete!(S, str) only matches channels where str is the exact ID. This is an efficient way to remove unresponsive subnets and unwanted channel types, but beware of clumsy over-matching.
Merge¶
SeisData structures can be merged using the function merge!, but this is much more complicated than addition.
-
merge!(S)
- Does nothing to channels with unique IDs.
- For sets of channels in S that share an ID… + Adjusts all matching channels to the :gain, :fs, :loc, and :resp fields of the channel the latest data + Time-sorts data from all matching channels by S.t + Averages data points that occur simultaneously in multiple members of the set
- throws an error if joining data that have the same ID and different units.
Keeping Track¶
Because tracking arbitrary operations can be difficult, several functions have been written to keep track of data and operations in a semi-automated way.
Taking Notes¶
Most functions that add or process data note this in the appropriate channel’s :notes field. However, you can also make your own notes with the note! command:
-
note!(S, i, str)
Append str with a timestamp to the :notes field of channel number i of S.
-
note!(S, id, str)
As above for the first channel in S whose id is an exact match to id.
-
note!(S, str)
if str* mentions a channel name or ID, only the corresponding channel(s) in **S is annotated; otherwise, all channels are annotated.
Clear all notes from channel i
of S
.
clear_notes!(S, id)
Clear all notes from the first channel in S
whose id field exactly matches id
.
clear_notes!(S)
Clear all notes from every channel in S
.
Keeping Track¶
A number of auxiliary functions exist to keep track of channels:
-
findchan
(id::String, S::SeisData)¶
-
findchan
(S::SeisData, id::String)
Get all channel indices i in S with id \(\in\) S.id[i]. Can do partial id matches, e.g. findchan(S, “UW.”) returns indices to all channels whose IDs begin with “UW.”.
-
findid
(S::SeisData, id)¶
Return the index of the first channel in S where id = id.
-
findid
(S::SeisData, Ch::SeisChannel)
Equivalent to findfirst(S.id.==Ch.id).
-
namestrip!(S[, convention])
Remove bad characters from the :name fields of S. Specify convention as a string (default is “File”):
| Convention | Characters Removed:sup:(a) |
|---|---|
| “File” | "$*/:<>?@\^|~DEL |
| “HTML” | "&';<>©DEL |
| “Julia” | $\DEL |
| “Markdown” | !#()*+-.[\]_`{} |
| “SEED” | .DEL |
| “Strict” | !"#$%&'()*+,-./:;<=>?@[\]^`{|}~DEL |
(a) DEL
is \x7f (ASCII/Unicode U+007f).
-
timestamp
()¶
Return current UTC time formatted yyyy-mm-ddTHH:MM:SS.μμμ.
-
track_off!(S)
Turn off tracking in S and return a boolean vector of which channels were added or altered significantly.
-
track_on!(S)
Begin tracking changes in S. Tracks changes to :id, channel additions, and changes to data vector sizes in S.x.
Does not track data processing operations on any channel i unless length(S.x[i]) changes for channel i (e.g. filtering is not tracked).
Warning: If you have or suspect gapped data in any channel, calling ungap! while tracking is active will flag a channel as changed.
Source Logging¶
SeisIO functions record the last source used to populate each channel in the :src field. Typically this is a string.
When a data source is added to a channel, including the first time data are added, this is recorded in :notes with the syntax (timestamp) +src: (function) (src).


Relieve stress from you pinkies by moving Escape
, Enter
,
-
, =
, [
and ]
to the modifier keys, without
losing the ability to use them as modifier keys.
Note: Using Shift as a dual key is mainly intended for keyboards that let you use Shift with your thumbs. You can still use it on regular keyboard, but you're not going to get as much benefit from it


GEOS
Open in Google Earth (KML)
Map
Measure
Print
Measure:
distance
area
clear
done
Print:
Paper Format
A4 landscape
A4 portrait
A3 landscape
A3 portrait
Zoom Level
print
done


Carbs
10g (1/202) butter 50g (202) onion, very finely chopped 450g (11b) beef (flank, chump or shin would be perfect), freshly minced 1/2 teaspoon fresh thyme leaves 1/2 teaspoon finely chopped parsley 1 small organic egg, beaten salt and freshly ground pepper pork caul fat (optional) oil or dripping


Date of Birth: 05.02.2005
Place of residence: Ukraine, Kyiv
Educational institution: Financial Lyceum
Front-end technologies:


{% import 'bootstrap/wtf.html' as wtf%}
sign up to receive random quotes to your email when they are uploaded


Configuring Google Analytics
How to configure either the Google Analytics module.
You must have an existing Google Analytics account. Visit the Google Analytics website at http://www.google.com/analytics and click the Create an Account button.
- Navigate to Admin > Advanced Settings > Google Analytics - OR - Go to a Google Analytics module.
- In the Tracking ID text box, enter the website tracking ID that you obtained from the welcome message sent by Google when you signed up for an account.
- Optional. In the URL Parameter text box, enter the JavaScript code fragment that will be passed as a parameter to the page tracker. This is used to customize the value tracked in Google Analytics instead of the default location.
-


The goal of the Conversational Coding team is to broaden computer literacy. We help beginners find the right level of understanding they need to get comfortable with learning how online technologies work. As experienced programmers, who have been at this for many years, we have seen a variety of tools being introduced that make the introductoon to coding a far gentler one than it used to be. Our lessons help beginners get familiar with these tools, and learn how to use them as stepping-stones to graduate to a deeper understanding of how computer programs work.
Our main focus is on “dynamic web applications” - something you can make accessible on the Internet for people to use via their browsers and that stores user data of some sort. We believe that the Ruby on Rails framework is the best way to be introduced to the various concepts involved. Specifically, our lessons focus on the following topics:
Have questions or suggestions? Feel free to add to the conversation on GitHub or ask us on Twitter.
Have fun coding!


OBWriteInternational2_Data_Initiation_ExchangeRateInformation
- ContractIdentification : String
Unique and unambiguous reference to the foreign exchange contract agreed between the initiating party/creditor and the debtor agent.
- ExchangeRate : BigDecimal
The factor used for conversion of an amount from one currency to another. This reflects the price at which one currency was bought with another currency.
- RateType : String
Specifies the type used to complete the currency exchange.
- Enum:
- Actual
- Agreed
- Indicative
- UnitCurrency : String
Currency in which the rate of exchange is expressed in a currency exchange. In the example 1GBP = xxxCUR, the unit currency is GBP.


{% extends 'base.html' %} {% load imagekit %} {% block content %}
Created at {{ portfolio.created }}
{% if portfolio.description %} {{ portfolio.description|linebreaksbr }} {% else %} No description was provided. {% endif %}


Hi everyone my name is zakeyah abu yasein , i'm 27 years , i studied counseling and mental health in in the university of jordan and graduated since 2015 mother of two girls gazl and laten , my favorite color is black , never traveled anywhere and i hate winter , my goals is to have a good career travel as much as i can


{% extends 'base.html'%} {% block styles%} {{ super() }} {% endblock %} {% block content %}
Where you can submit your pitches and other users will vote on them and leave comments to give their feedback on them


November 23 2017
This is my first article
Ham hock flank leberkas porchetta filet mignon kevin. Short loin short ribs hamburger, flank sausage pork chop venison filet mignon. Andouille porchetta shank salami, drumstick hamburger sirloin burgdoggen biltong. Tail drumstick doner pancetta flank chicken beef pork chop shank. Bacon turducken pork tenderloin burgdoggen. Swine pastrami beef ribs shank porchetta sausage.
Kevin pork loin ribeye pork alcatra ball tip cupim brisket bacon. Short ribs bacon brisket ball tip bresaola andouille. Flank jowl venison tail. Meatloaf pastrami tenderloin cupim tail, salami shankle corned beef sausage boudin. Turducken biltong jowl chicken meatloaf.
Ribeye kevin pork belly andouille, boudin shoulder ball tip corned beef jerky.  Ground round flank kielbasa tail shoulder pancetta. T-bone kielbasa filet mignon meatloaf ribeye jerky frankfurter jowl meatball. Shoulder meatloaf porchetta jerky shankle, chuck landjaeger cupim alcatra. Salami picanha andouille sausage cupim t-bone, ground round turkey capicola beef ribs. Pork loin pastrami short loin burgdoggen, ball tip tongue strip steak.
November 27 2017
This is my Another article
Ham hock flank leberkas porchetta filet mignon kevin. Short loin short ribs hamburger, flank sausage pork chop venison filet mignon. Andouille porchetta shank salami, drumstick hamburger sirloin burgdoggen biltong. Tail drumstick doner pancetta flank chicken beef pork chop shank. Bacon turducken pork tenderloin burgdoggen. Swine pastrami beef ribs shank porchetta sausage.
Kevin pork loin ribeye pork alcatra ball tip cupim brisket bacon. Short ribs bacon brisket ball tip bresaola andouille. Flank jowl venison tail. Meatloaf pastrami tenderloin cupim tail, salami shankle corned beef sausage boudin. Turducken biltong jowl chicken meatloaf.
Ribeye kevin pork belly andouille, boudin shoulder ball tip corned beef jerky.  Ground round flank kielbasa tail shoulder pancetta. T-bone kielbasa filet mignon meatloaf ribeye jerky frankfurter jowl meatball. Shoulder meatloaf porchetta jerky shankle, chuck landjaeger cupim alcatra. Salami picanha andouille sausage cupim t-bone, ground round turkey capicola beef ribs. Pork loin pastrami short loin burgdoggen, ball tip tongue strip steak.


As surprising as it may seem, writing code is just a small chunk of software development process. As you gain experience as a developer, you realise most of your time is not spent writing new code. You'll find yourself reading the code far more often than writing it.
Of course, you program new apps and features - but afterwards, there's debugging stage when you have to read the code in order to fix it. When you develop an app in a team you have to read and understand your teammates' code. Then there's DRY principle, code reviews, refactoring, support for legacy systems and finally professional development - all require reading code. Since the majority of development time is spent reading, code readability has a tremendous impact on time, costs and quality of any software project.
I believe that producing readable code is a skill that every developer should actively work on. Most of the today's projects require multiple people working together and it is long-established that teamwork is more import than individual skill. In this short article, I'll present established practices and some of my own observations on writing eloquent code. Although it's focused on JavaScript, developers from other ecosystems may also find some points useful. I'm open for debate so please expect this article to evolve over time.
ES6 JavaScript had been used in code examples.
When you write your code always think about the future reader. Practice mentality of looking at the code from the perspective of someone who sees it for the very first time. Is this easy to understand? May this be misinterpreted? Is this easy to follow? Your goal is to produce code that will be understood even by someone who doesn't pay much intention while reading.
Like a manual - clear instructions, not many ways to misinterpret developer's intentions.
It's hard to be eloquent if you barely know the language. As it is with spoken tongues learning basic vocabulary may be enough to communicate your ideas - but it requires more focus from the listener which may be wearing in the long run. Therefore the first step towards writing quality code is learning the complete language. Complete means not only the easy parts but also the complicated parts, tricky behaviours and common patterns.
As an example: Array.reduce()
seems quite complicated first time you read about it. If you decide to skip this part you will find yourself numerous times reading the code you don't understand. You will also write code like this:
const numbers = [1, 2, 3, 4]
let sum = 0
for (let i = 0; i < numbers.length; i++) {
sum += numbers[i]
}
...when you could simply write:
const numbers = [1, 2, 3, 4]
const sum = numbers.reduce((acc, number) => (acc + number), 0)
Coming back to this code months later, in order to have a clear understanding of the first example you have to:
The second example is shorter, easier to understand (if you know how Array.reduce()
works that is), but most importantly - it describes the intent of the developer. Take the array and reduce it to a single value. Nothing more, nothing less.
I've seen code utilising two for loops and one while loop, where single Array.map()
was sufficient. Learning the language may save you hours in both writing and reading the code. It will also make your job much easier.
English may not be most common language in the world, but it provides many advantages:
Use English to name your variables, functions and write your comments. Eventually, people from around the world may join your project and it would be rude to ask them all learn Norsk.
When you write your code you have to keep every variable and every function you use in your short-term memory. That's why you understand what val.gc = oppr[0]++
does. But after a week this knowledge is long gone. At this point, no one in the world knows what this code does, and if so happens that you have to debug it - you will have to learn this code. You will have to follow it line by line often for hundreds of lines to find the one you're interested in.
Consider this example. Do you know straight away what this code does?
this.ctrlBar.el().insertBefore(this.ctrlBar.fullscreenToggle.el())
And how about these 3 lines ?
const bar = this.controlBar.element()
const toggle = this.controlBar.fullscreenToggle.element()
bar.insertBefore(toggle)
Don't hassle to create tonnes of variables just to make code easier to read. If you'd come across code from the first example you would have to stop and decipher all those brackets and dots. In the second example, I added some extra lines but you should be able to understand my intent almost like if you'd be reading it in plain English: insert bar before toggle.
With time you'll learn to skip these variable definitions and read only the part responsible for app's logic. If you'll ever need to check what this toggle actually is - you can still look for its definition couple of lines above. But this knowledge is not forced on you if you're not interested like in the previous example.
It's easy to write a line of code like this:
const topValue = ($(window).scrollTop() / 3) - Math.abs(event.data.offset)
Sometimes situation requires a bit of trial and error or fiddling with values in order to find something that suits our needs. It mostly happens working on graphics, animations or other dynamic actions on the page. Whenever you are finally happy with the result consider refactoring the code for better readability. Above example:
This is too complex and should be broken into smaller, individual statements:
const position = $(window).scrollTop()
const desiredPosition = position / 3
const jump = Math.abs(event.data.offset)
const topValue = desiredPosition - jump
Desirably each line states one fact, describes one operation or tests one condition. Sometimes you may place two coupled operations on a single line if that improves readability. In the example above, getting an absolute value from the offset and assigning it to a variable are technically two operations - but splitting it into multiple lines wouldn't bring any benefits to clarity. Definitely, avoid three and more operations in a single line.
Consider these two examples:
const truncateText = (text, size) => {
const fragment = text.substr(0, size).trim()
if (size !== text.length && fragment.charAt(fragment.length - 1) !== '.') {
return fragment + '…'
}
return fragment
}
const truncateText = (text, size) => {
const isTruncated = size !== text.length
const fragment = text.substr(0, size).trim()
const lastCharacter = fragment.charAt(fragment.length - 1)
const endsWithPeriod = lastCharacter === '.'
if (isTruncated && !endsWithPeriod) {
return fragment + '…'
}
return fragment
}
The heart of this function is a simple if
statement. In the second example, there is a clear distinction between preparation and execution. First, I prepared the variables I need to describe what the function intends to do, then I executed logic. This way function is easy to understand and when you need more details - they are available for you. Function definition in plain English: add the ellipsis only when text got truncated and doesn't end with period.
In the former example, everything happens on the fly. The function is surely shorter but you'll spend much more time trying to understand it.
Keep your functions short and single-purposed. As with variables, consider creating functions for readability purposes. In the example from the previous paragraph I would go a bit further and write the code this way:
const endsWithPeriod = (text) => {
const lastCharacter = text.charAt(text.length - 1)
return lastCharacter === '.'
}
const styleFragment = (fragment) => {
const needsEllipsis = !endsWithPeriod(fragment)
if (needsEllipsis) {
return fragment + '…'
}
return fragment
}
const truncateText = (text, size) => {
const isTruncated = size !== text.length
const fragment = text.substr(0, size).trim()
if (isTruncated) {
return styleFragment(fragment)
}
return fragment
}
This code will probably seem a bit off for you at first. This code is much longer than the first example and it takes bit more effort to write. But it's much easier to debug, much easier to build upon and much less prone to errors. The extra effort you put into writing this code will benefit you every single time you have to go back to it.
Every function has a clear single purpose, so whenever you have to rework one of the behaviours you can focus on this single behaviour alone. If stylising the truncated fragments requires more features you can add them in styliseFragment function without worrying out how the whole truncation works in the first place.
As you break functions into smaller and smaller bits you have to remember to keep them in some sort of hierarchy. Higher order functions should be kept together and provide a general picture of what given file/class/controller want's to achieve. They should break into smaller functions that describe on how given operation is being achieved. These also should break into smaller ones for trivial tasks.
Never mix trivial and higher order operations together.
class Modal {
constructor (refs) {
this.elements = this.getElements(refs)
this.prepareModal()
this.openModal(this.elements[0])
}
prepareModal () {
this.attachEvents()
this.updateStyles()
}
// ...
The constructor
of the class above hardly does any work but it provides a clear and short explanation of what it does - it needs a bit of bootstrapping before showing the first element passed to it. Also, middle-man function prepareModal
doesn't do anything by itself - only describes what steps are needed to prepare this modal.
componentWillReceiveProps(nextProps) {
this.setFilters(nextProps)
this.setActiveOptions(nextProps)
this.setReady()
}
componentWillReceiveProps
is an important method in React. As a fundamental part of the framework - it's almost guaranteed a developer trying to understand the component will look there first. That's a clear hint that this method should be considered higher order - only the bigger picture is introduced here.
if
statements are one of the most basic features of programming. At the same time, they are the most complicated part of any application. if
statements create crossroads where the flow of the app may take multiple paths. The more if
statements the more paths you have to control. To efficiently debug an algorithm you need to know all different ways data may be manipulated. That's why it's so important to treat if
statements with extra care.
Other developers should be able to follow the flow of your app just by briefly scanning it. Never use more than two conditions in one if
statement - create a function instead. If there's one condition but it's a complicated one - also use a function to explain it.
Examples:
if (isNumber(value) && value > 5 && value < 10) {
// some code
}
const isInRange = (value, min, max) => {
if (!isNumber(value)) {
return false
}
return value > min && value < max
}
if (isInRange(value, 5, 10)) {
// some code
}
If you have to meet two conditions at once try to reflect that in the if
statement itself.
if ($('div').hasClass('selected') && $('div').attr('hover') == true) {
// some code
}
const isSelected = element => $(element).hasClass('selected')
const isHovered = element => $(element).attr('hover')
if (isSelected(element) && isHovered(element)) {
// some code
}
Do not ever nest for
loops, while
loops, map
-s, filter
-s, switch
-es or if
statements - in any configuration of these. It happens often because it's most natural way of writing code. Unfortunately every time someone else will have to go through code like that - he'll have to go line by line multiple times to digest all the operations happening at the same time. Use multiple functions to divide and conquer.
elements = elements.each(function(element){
element.attributes.filter(function(attribute){
if (isNumber(attribute) && attribute > 0) {
return true
}
return false
});
});
const isPositive = attribute => (
isNumber(attribute) && attribute > 0
)
const keepPositiveAttributes = element => (
element.attributes.filter(isPositive)
)
elements = elements.each(keepPositiveAttributes)
Notice naming: keepPositive
wouldn't work in this example as we don't want to keep only positive elements - we want to keep all elements but only with positive attributes along with them. The operation is a bit tricky so we absolutely need to use a longer variable name.
All block elements (including: if
, for
, when
& functions) should have no more than 8 lines of code. Otherwise, it's hard to answer some questions:
this
?}
needed here at all?Long blocks make code really hard to read and debug. Especially with long if blocks you may find yourself thinking if the statements you are looking at are still part of the conditional route or not.
There are only two hard things in Computer Science: cache invalidation and naming things. When you code, you know what any given variable is intended to - why it is needed and what value it is supposed to keep. You don't really need descriptive variable names that much. Use variables for future reader's benefit. Consider these two examples:
if (checkAttr(img)) {
panEl.active = img
}
if (imageIsSelected(image)) {
panel.visibleItem = image
}
Notice the mind shift between these examples. In order for someone to understand this if
statement - he needs to understand the condition. The condition relays on an image being selected or not. The details of implementation (checking for some attributes) are not relevant at this moment. The same thing applies on line 2. Attribute active
doesn't tell much. The panel is active, but what does that really mean? How does that affect other parts of the app? What happens when I change it? Just by reading the second snippet you probably already have answers to all those questions.
You may understand what this.ctBar.pan()
is in the heat of the moment, but when you'll come back to this code in 6 months time you'll appreciate using this.controlBar.panel()
as your names.
The only exceptions are the most commonly used abbreverations:
i
for index in iterators val
for value in iteratorsacc
for accumulator in Array.reduce()refs
for DOM references el
for DOM elementobj
for not-specified object attrs
for attributes …and only if the function you use it in is really easy to understand otherwise. For example:
let indexes = [1, 2, 3, 4]
let isCorrect = indexes.reduce((acc, val, i) => (acc && val > i), true)
As you can see above, even one-line function may be hard to understand. It could be beneficial to write more verbose:
let indexes = [1, 2, 3, 4]
let isCorrect = indexes.reduce((previous, current, index) => {
let currentIsCorrect = current > index
return previous && currentIsCorrect
}, true);
…and it's now more clear that for some reason isCorrect
means that every value in the array is bigger than its index.
Also, you should avoid abbreviations in functions with extended preparation. Example:
const getHex = (ref) => {
const s = `[${ref}]`
const el = car.find(s)
const attr = el.getAttribute('color')
return hex(attr)
}
const getHexColor = (reference) {
const selector = `[${reference}]`
const element = carousel.find(selector)
const color = element.getAttribute('color')
return convertToHex(color)
}
Try to use as short variable names as possible. Both camelCase
and snake_case
are hard to read in sentences, therefore, you must keep your names short. Single words may not always be enough to describe your state, but try to keep the use of two- and three-word names to a minimum. Never use more than three words in a single variable name. Use shorter variable names even if it means being a bit too generic. It's always easier to dig into specifics when you understand the logic than not understand anything in the first place.
Example of code where author tried to be extremely precise:
function getTruncatedTextStartPosition(firstMatchPosition, firstMatchLength, text) {
const lengthOfStringToTheRightOfTheMatch = text.length -
(firstMatchPosition + firstMatchLength)
if (lengthOfStringToTheRightOfTheMatch < Search.DEFAULT_PADDING) {
return firstMatchPosition - (Search.DEFAULT_PADDING +
(Search.DEFAULT_PADDING - lengthOfStringToTheRightOfTheMatch))
}
return firstMatchPosition - Search.DEFAULT_PADDING
}
After refactoring could look like:
function getFragmentStart(stringStart, stringLenght, text) {
const padding = Search.DEFAULT_PADDING
const start = stringStart - padding
const end = stringStart + stringLenght
const reminder = text.length - end
if (reminder < padding) {
const offset = padding - reminder
return start + offset
}
return start
}
Logic is sill a bit hard to follow and doesn't make much sense without the context. But the reader is able to quickly scan the code and decide if this is something he's supposed to focus on. In this example, I even decided to give a new name to a global constant - as it makes it easier to read. I also used preparation/execution pattern mentioned earlier.
As you use variables and function names to explain your code, the number of comments will drastically decrease. In the ideal scenario, there would be no need for comments at all. So that everyone would be able to read through the app in one go - without any interruptions. Unfortunately, in the real world, some parts of code require some extra explanation. Is it because of a quirk of the system, some complicated operation or code usually described as: magic.
// All usernames saved in the database
// contain account name, so we have to remove it.
username = username.substr(0, username.lastIndexOf(account))
These cases are extremely rare. Try not to use comments, definately do not comment obvious things:
// Variable for indicating first carousel change when it opens
let firstCarouselChange = false
You could write instead:
let carouselUsed = false;
Aside from functions used for stylistic purposes, most functions simply compute values. It means they take some values in, change it slightly and pass a new value back. Functions should never change any value from outside of its body. For TDD, preventing bugs and improving code readability it's important to keep it this way as much as possible. Pass arguments it, return values out. Take a look at following code:
let sentence = 'small letter and no full stop'
fixStyling()
function fixStyling() {
sentence = sententce.split(0,1).toUpperCase() + sentence.split(1) + '.'
}
let sentence = 'small letter and no full stop'
sentence = fixStyling(sentence)
function fixStyling(sentence) {
return sententce.split(0,1).toUpperCase() + sentence.split(1) + '.'
}
In first example function changes variable outside its scope. This is not only hard to follow, but also produces a lot of bugs. In second example function takes some value, changes only data it took, then returns the new value. It's much easier to reason as every function acts in its own sandbox.
Of course, other points from this article could be applied to refactor it even more:
const fixStyling = (sentence) => {
const firstLetter = sententce.split(0, 1)
const rest = sententce.split(1)
return firstLetter.toUpperCase() + rest + '.'
}
let sentence = 'small letter and no full stop'
sentence = fixStyling(sentence)
Once again person working with this code may can variables' definitions and read function logic resembling English: Take first letter in uppercase, then rest of the sentence and add comma at the end.
Regexes are not as scary as many people think. They are a great tool, and when treated properly they can be easily accessible to anyone in the team. The single regex should always get its own function. If you wrote the regex yourself, try to explain how it works flag by flag - for future debugging. In the end, that may be the last time someone understands how it works. If you googled it - add a link to the page. Be generous with your comments, add explanation and examples.
An example of code that no one will ever touch in fear of breaking it:
text = text.replace(/\[([A-Z]{3,6})([0-9]{5,6})\]:?(LINK)?/gi), convertToLinks)
Should look like this:
const findCodesRegex = () => {
// Selects all occurences of special codes in text
// i.e. [FILE02966] or [IMG00066]:LINK
// /
// \[ - opening bracket
// ([A-Z]{3,6}) - group 0: code (3 to 6 letters)
// ([0-9]{5,6}) - group 1: serial number (5 to 6 numbers)
// \] - closing bracket
// :?(LINK)? - group 2: optional link parameter
// /gi - global and case insensitive
return /\[([A-Z]{3,6})([0-9]{5,6})\]:?(LINK)?/gi
}
text = text.replace(findCodesRegex(), convertToLinks)
Final operation is calculated before function returns value:
const makeStars = (quantity) => {
const elements = new Array(quantity + 1)
const stars = elements.join('*')
return stars
}
const makeStars = (quantity) => {
const elements = new Array(quantity + 1)
return elements.join('*')
}
You don't need else
when if
block ends with return
:
const findLonger = (first, second) => {
if (first.length > second.length) {
return first
} else {
return second
}
}
const findLonger = (first, second) => {
if (first.length > second.length) {
return first
}
return second
}
Use conditional operator to conditionally return a value. From the previous example:
const findLonger = (first, second) => {
const firstIsLonger = first.length > second.length
return firstIsLonger ? first : second
}
Be explicit when casting to other types. !!
trick is neat but it's easy to miss and may confuse less experienced coders:
let value = 3
let textValue = value + ''
let condition = !!textValue
let value = 3
let textValue = String(value)
let condition = Boolean(textValue)
Never mix logical operators in a single line. Divide them into variables:
const checkHusband = (tall, muscular, handsome, rich) => {
return tall && ((muscular && handsome) || rich)
}
const checkHusband = (tall, muscular, handsome, rich) => {
const goodLooking = muscular && handsome
const prospective = goodLooking || rich
return tall && prospective
}
This style of coding isn't natural to anyone. You'll probably make mistakes in the process. I write ugly code quite often - but at the same time, I'm actively trying to improve. Keep practising. Keep attention to details. If you're happy with how your code works - refactor it for better readability. If you debug some code and you had a hard time understanding it at first - refactor it for next occasion. If you work in a team - ask someone for code review. Step by step, function by function your code will become better. Have fun!


| categories:development
Using Phoenix with docker, Part 2: Implementation
This is part two of a three part series: Part 1 - Part 2 - Part 3
Contents
- Installation
- Let’s go
- Scaffolding
- Simpler times
- Providing a “frontend”
- New routes
- Reading the documentation
- Handle the file
- Resizing images
- Resizing
- Problems of the demo app
- Conclusion
Installation
Before we start, please make sure you install Elixir and Phoenix. If you do not care or have already installed both, you can skip the next section.
Elixir
Installing Elixir is actually not too difficult - it’s not as convenient as just typing
sudo apt-get install elixir
as it equires the installation of Erlang and the Open Telephony Protocol (OTP). A more detailed guide on how to do the installation of Elixir (including the installation of Erlang/OTP can be found on the homepage - regardless of your preferred OS-choice.
Phoenix
Once you have installed Elixir, it’s time to setup Phoenix. This can be done via hex
, which in turn can be installed via mix
:
mix local.hex
and then installing Phoenix via:
mix archive.install https://github.com/phoenixframework/archives/raw/master/phoenix_new.ez
If you need more detail, consider visiting the Phoenix docs.
Let’s go
mix phoenix.new kitteh
should create a new Phoenix project for you in the folder ./kitteh
. When asked to install dependencies, you should probably say yes, although it’s irrelevant since we’re not going to build a complex frontend (but there is additional css, I promise).
You should end up with something like this:
.
├── brunch-config.js
├── _build
├── config
├── deps
├── lib
├── mix.exs
├── mix.lock
├── node_modules
├── package.json
├── priv
├── README.md
├── test
└── web
If you do not wish to do anything yourself, I prepared a repository here. You may use the tag 01-lets-go
to get the initial codebase.
Scaffolding
Scaffolding is a pretty fast and reliable way in Phoenix to get off the ground. We’re not going to use it to its full potential here.
Consider generating a controller:
mix phoenix.gen.html --no-model Image images
This might be a bit counter-intuitive (it is to me) - but generating just a controller and its views requires the all-including gen.html
task, which normally generates a complete resource with views associated, complete with model files included.
Looking at our newly generated controller, we notice that it has been filled with all kind of good stuff:
defmodule Kitteh.ImageController do
use Kitteh.Web, :controller
alias Kitteh.Image
plug :scrub_params, "image" when action in [:create, :update]
def index(conn, _params) do
images = Repo.all(Image)
render(conn, "index.html", images: images)
end
def new(conn, _params) do
changeset = Image.changeset(%Image{})
render(conn, "new.html", changeset: changeset)
end
def create(conn, %{"image" => image_params}) do
changeset = Image.changeset(%Image{}, image_params)
case Repo.insert(changes
# [...]
Woah.
Hold your horses. All I wanted was a simple controller with an action or two.
So, let’s rollback the changes. There is no convenient way (i.e. reverse generators) to do this yet, so
git clean -f && rm -rf web/templates/images
to the rescue. Phew.
Simpler times
Let’s fall back to the already generated PageController
. It already has an action index
ready to use.
At the moment it renders a file called index.html.eex
(link for the lazy). It constitutes a demo partial that together with the app.html.eex
(this one here) forms a complete webpage at the /
route.
We can look at it on the locally running instance at localhost by executing
iex -S mix phoenix.server
Neat.
Providing a “frontend”
This should be easy.
Phoenix includes Bootstrap by default. I could disagree with that, but then again, using Bootstrap is not to inconvenient.
The Phoenix team apparently decided to delegate the frontend choices to the userbase. A wise choice in the short term, as the whole frontend sector is quite fragmented at the moment (early 2016).
That being said, replacing everything in index.html.eex
with

Kitteh uploader

Upload a cat picture. Doggies are welcome, too.

<form action="/upload" method="post" enctype="multipart/form-data" class="form">

Image
<input type="file" required id="image" name="image" class="form-control">

</form>

should do the trick.
No, we are not using the form builders that Phoenix provides, but feel free to read up on them. We can just use plain HTML instead.
If that is all too much frontend stuff for you, i suggest you look at the 02-simple-frontend
tag here.
New routes
We defined an /upload
path that the form uses, but this route is nowhere to be found. Let’s add it:
# see web/router.ex
# [...]
scope "/", Kitteh do
pipe_through :browser # Use the default browser stack
get "/", PageController, :index
# the new route
post "/upload", PageController, :upload
end
# [...]
Note I also sneakily added a required
attribute to the file-input
, to avoid any validation concerns.
If we try to send the form n… we notice that we’re missing a way to submit the form and add a button first.
If we try to send the form now, it will crash, since no action will take care of the request.
But wait, how do I upload stuff anyway? Should the framework not provide me with some way to make this easier?.
Kids, read your documentation before heading into battle.
Reading the documentation
Turns out, the Phoenix people do provide something to do file uploads.
That means we can actually use the form builders after all since we’re going the changeset route.
I feel silly.
Create a new model:
# note that this is just my initial try, have a look at the migrations for the actual fields used
mix phoenix.gen.model Image original_name:string url:string size:integer
and migrate the database:
mix ecto.create && mix ecto.migrate
If this fails for you, make sure you have a valid configuration for you database. To configure your database, see your local config/dev.exs
(see here).
So, finally, we can create a changeset in the controller and use it in the template:

<%= form_for @changeset, "/upload", [multipart: true], fn f -> %>

Image
<%= file_input f, :file, required: true %>

<%= submit "Upload", class: "btn btn-primary" %>

<% end %>
The @changeset
is introduced and passed to the view in the controller:
# web/controllers/page_controller.ex
# [...]
alias Kitteh.Repo
alias Kitteh.Image
def index(conn, _params) do
changeset = Image.changeset(%Image{})
render conn, "index.html", changeset: changeset
end
# [...]
If all goes well, this should render our frontend again. I also snuck in the missing Button for submitting the form. Please note that in order to actually use the line
<%= file_input f, :file, required: true %>
a virtual file
attribute has to exist in the Image
model.
If this was all just ramblings of a mad developer for you, you can also check out the tag 03-actually-read-the-docs
here.
Handle the file
It’s time for some action in the controller, because at the moment our application will crash if we try to submit the form with an image.
The controller action has to do the following:
Validate the file given- Move the uploaded file under a new name into a folder we can access
- Save the model with some information on the file uploaded to the DB.
- if that was successful, redirect to the
show
action for the new image - (alt) if not, redirect to the
index
with a message
I will skip the validation on the file - one could do this by validating the size
field of the changeset before inserting.
We’ll skip this here and assume that the file given is something we want.
Phoenix will give us the file as a Plug.Upload
struct in our params
to the newly created upload
function in PageController
:
# web/controllers/page_controller.ex
# [...]
def upload(conn, params) do
# now what?
end
# [...]
Thinking like a Rails developer, the fat model approach comes to mind. Let’s put all the logic for this into a model and let the controller action pass in the params. Be done with it, move on. Have a beer maybe.
This is not viable here, since Elixir ultimately does not care where your functions live. There are no models, just functions and structs.
I decided in favour of a more controller based approach. The controller will do the the copying and transform the file input into a usable params
map:
# web/controllers/page_controller.ex
# [...]
def upload(conn, %{ "image" => %{ "file" => file } }) do
# transform the uploaded file into a changeset
params = file
|> copy_file(unique_name)
changeset = Image.changeset(%Image{}, params)
# try to insert the newly generated changeset
end
# [...]
The copy file function acutally does the more “heavy lifting”:
defp copy_file(file) do
extension = Path.extname(file.filename)
target = target_path <> name <> extension
case File.copy(file.path, target) do
{:ok, size} ->
%{
generated_name: name,
token: String.downcase(name),
path: target,
original_name: file.filename,
content_type: file.content_type,
size: size
}
{:error, _} ->
%{}
end
end
Depending on whether the copying was successful, we either get a proper params
map with all the necessary information filled in or we are left with an empty map that will never pass our validations.
Note that target_path
actually behaves differently from what you would expect. In Phoenix, you do not find the same behaviour as with Rails’ Rails.root
.
For now, we need a target path that lives within our application and we can access. But our codebase will be compiled (in contrast to a Ruby codebase), so we cannot be sure where our bytecode ends up (Hint: It’s in the ./_build
folder).
We can do this though:
Application.app_dir(:kitteh, "priv")
See this StackOverflow answer for more information. The actual implementation used is found here.
After having generated a params
map, the rest is just the same as in any Phoenix tutorial you might find:
- generate a changeset based on
Image
- insert that changeset
- redirect to the show action or abort and rerender the index template
In case you were wondering:
The shorthand generated for the kitty is generated via Image.generate_unique_name
, which uses collected seed data to generate a different combination of these attributes. We have to try again if we actually used the name before in the database. Since Elixir does not have any loops, we resort to recursion until we find a name that we can use.
Note: This has no safety measures - if all combinations of the attributes are used up, our database is “full” and we are screwed.
After the image is persisted we redirect to show
. Additionally, an ImageController
is introduced with a show
action here to actually serve up the image for now. This is intermediary - ultimately, we’ll not use Phoenix to serve assets in “production”.
If all goes well, the upload should work and the original image should be served under a memorable shorthand.
Shortcuts
If this is all to much coding and you would like the easy way out, check out the tag 04-enable-uploading
here.
Resizing images
Remember the image modifiers? Like “Tiny”, “Large” and “Monstrous”? We forgot about those.
It would be nice if we had all the images for the different sized images pre-generated. We could use the same mechanisms we already have implemented to serve them.
GenServer
In a (newer) Rails environment, we could utilize anything that fulfills the interface of ActiveJob, like an adapter to Sidekiq or the delayed_job
gem gem. We basically spin up a second OS process to generate the image, regardless of the solution.
Not an option here though. There are some solutions to queues and background jobs, but we are on the Erlang VM anyway, so we can utilize the technology available to us. After all, BEAM processes are cheap and lightweight.
GenServer might be the answer. But actually having a long running process in the background that we can use as a service might be overkill here.
Let’s use Task
instead. Task
is a wrapper around Elixirs spawn
function and can be used for a multitude of things that are actually more advanced than we do right here, right now.
Looking into the code:
defp create_sizes(image) do
sizes = %{ "Tiny" => "90", "Large" => "300", "Monstrous" => "600" }
original_file = image.path
Enum.each sizes, fn({ label, size }) ->
Task.start fn ->
name = label <> image.generated_name
file_params = resize(image)
|> copy_file name
changeset = Image.changeset(%Image{}, file_params)
|> Repo.insert
end
end
end
Using Task.start
creates a subprocess that is not linked to the current process. Process linking here is not strictly necessary, as this is implemented as a fire-and-forget strategy. In contrast to Task.start_link
, we’re not linking our main process (kitteh
) to the new process. In case it crashes, we do not want to tear down our application (process) as well.
Note: I had some problems finding out on how to match function call in using Enum.each
against the result of the map. The resulting argument is matched against a tuple. Might be trivial, but just in case you were wondering.
This has the notable disadvantage every fire-and-forget strategy has - we do not know if we actually create the images. Good enough for this application, but for something production-ready, one should look for some bidirectional communication. Just in case, you know, you maybe want to connect these images to one another.
Resizing
Resizing images is something one should probably be too lazy to implement oneself. Enter mogrify
- it is a wrapper library for ImageMagick, providing us with functions for handling image-resizing.
We should make a mental note here as we introduce a hard dependency for our docker containers later on. Any container that we want to create for this application now has to provide this dependency.
With another commit, the resize function is introduced:
defp resize(image, name, size) do
new_path = target_path <> name <> Path.extname(image.path)
new_image = open(image.path) |> copy |> resize(size) |> save(new_path)
%{
generated_name: name,
token: String.downcase(name),
path: new_path,
original_name: image.original_name,
content_type: image.content_type,
size: image.size
}
end
This should create all the resized versions. We can also utilize the builtin Mogrify.copy
function to skip manual copying as we did for the initial image. We end up with returning params we can use to create another changeset and insert everything. The rest of the system should now work for the resized images as well.
Note: Somewhere around this point I noticed a screw up in the router as matching order was off. This lead to a redirect to /
after the initial image had been created.
At this point, our image uploader should be feature complete. Altough being the duct-tape ghetto version it now is, it should provide a good basis to play around with in the next part.
If this is all the same to you and you could not care less about how the images are generated and stored exactly, check out the tag 05-resizing-cats
here.
Problems of the demo app
This demo application has quite a few problems, some of them already discussed, some of them a little less obvious:
- no tests - this is a biggie and nothing to sweep under the rug. Since this is not intended for production purposes, we sweep it under the rug
- the amount of images uploaded is limited to the combination limit of the seed data
- Naming is somewhat bad
- no validations on
Image
changesets besides the required fields [...]
The list is not complete, but one can always find things to improve. For example, by just supporting another type
, e.g. “Doggy” in addition to “Kitty”, we could double the image capacity. We could also make sure that all images have been created using Task.await
.
Nevertheless, it should make a good demo app as it has almost everything - a web app, some need for a database, static data that has to be stored and served from somewhere. All the good stuff.
there is one major problem when it comes to Live reload. I personally am not a fan of such a feature, but it is included in Phoenix by default. I had to disable it in dev, since it interfered with the upload feature. the uploaded into a folder that is live reloaded apparently wasn’t the best of my ideas.
Conclusion
In the next part, we’ll finally look into using docker
to gain containers for our project and use docker compose
to orchestrate our system.
If you already forgot what this was all about, check out part 1 to get a re-introduction.