<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Erlend Klakegg Bergheim &#187; gae</title>
	<atom:link href="http://klakegg.net/tag/gae/feed/" rel="self" type="application/rss+xml" />
	<link>http://klakegg.net</link>
	<description></description>
	<lastBuildDate>Thu, 25 Nov 2010 09:43:45 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.4</generator>
		<item>
		<title>En titt på Google App Engine</title>
		<link>http://klakegg.net/2008/11/en-titt-pa-google-app-engine/</link>
		<comments>http://klakegg.net/2008/11/en-titt-pa-google-app-engine/#comments</comments>
		<pubDate>Fri, 21 Nov 2008 21:03:15 +0000</pubDate>
		<dc:creator>Erlend</dc:creator>
				<category><![CDATA[programmering]]></category>
		<category><![CDATA[cloud]]></category>
		<category><![CDATA[gae]]></category>

		<guid isPermaLink="false">http://blog.averlend.com/?p=42</guid>
		<description><![CDATA[Google kom tidligere i år med noe de kaller Google App Engine (GAE), en tjeneste som lar utviklere lage applikasjoner i Python som kan hostes hos Google. I motsetning til vanlige webhotell er det en del begrensninger som man ikke er vant til fra tidligere hva angår biblioteker. GAE tilbyr ikke en vanlig relasjonsdatabase (som [...]]]></description>
			<content:encoded><![CDATA[<p>Google kom tidligere i år med noe de kaller <a href="http://code.google.com/appengine/">Google App Engine</a> (GAE), en tjeneste som lar utviklere lage applikasjoner i Python som kan hostes hos Google. I motsetning til vanlige webhotell er det en del begrensninger som man ikke er vant til fra tidligere hva angår biblioteker.</p>
<div id="attachment_61" class="wp-caption alignright" style="width: 152px"><img class="size-full wp-image-61" title="Google App Engine" src="http://klakegg.net/wp-content/uploads/2008/11/appengine.gif" alt="Google App Engine" width="142" height="109" /><p class="wp-caption-text">Google App Engine</p></div>
<p>GAE tilbyr ikke en vanlig relasjonsdatabase (som MySQL, MS SQL osv) men noe som kalles Datastore, denne er i tillegg begrenset til å ikke hente ut mer enn maks 1000 poster fra en &#8220;tabell&#8221;. Biblioteket som Google stiller med støtter heller ikke sesjoner (sessions), men man kan bygge dette ut ved å legge ved egne biblioteker. En annen artig begresning er at koden ikke kan lese eller skrive til disk. Når dette er sagt stiller man med Memcache, noe som kan sammenlignes med delt minne tilgjengelig fra alle instansene som kjører (for Google kan finne på å kjøre opp applikasjonen på mange for å skallere).</p>
<p>Jeg oppdaget GAE gjennom en artikkel i <a href="http://www.pythonmagazine.com/">Python Magazine</a>, og fant det utrolig interessant, men av mangel på applikasjoner å utvikle ble det lagt litt på hylla helt til for noen dager siden når jeg kom over en <a href="http://www.youtube.com/watch?v=bfgO-LXGpTM">video på YouTube</a> som viste ting i praksis, og da måtte det bare testes.</p>
<p>For å vise litt av mulighetene i GAE skal jeg utvikle en liten applikasjon som viser et tilfeldig sitat fra datastore, og som lar Google sine brukere logge på for å legge inn nye sitater. Det skal også være en link til hvert sitat.</p>
<p><span id="more-42"></span></p>
<p>For å utvikle kan man laste ned App Engine SDK, og da Google støtter kun Python i skrivende stund blir dette utviklingsspråket som må benyttes.</p>
<p>Til å begynne med lager jeg en modell, en representasjon av &#8220;tabellen&#8221; jeg skal benytte.</p>
<pre class="brush: python;">
class Quote(db.Model):
id = db.IntegerProperty()
user = db.UserProperty(required=True)
name = db.StringProperty()
text = db.StringProperty(multiline=True, required=True)
timestamp = db.DateTimeProperty(auto_now_add=True)
</pre>
<p>Det er lite spennende her, men det er verdt å merke seg at UserProperty lar deg lage en representasjon av Google sine brukere. Så snart modellen er laget kan jeg begynne å legge inn data i den ved hjelp av kode. For å lage siden må jeg definere en klasse for dette.</p>
<pre class="brush: python;">
class Add(Base):
def get(self):
self.render(&quot;add.html&quot;)

def post(self):
q = Quote(
text = self.request.get(&quot;text&quot;),
name = self.request.get(&quot;name&quot;),
user = users.get_current_user()
).put()
self.redirect(&quot;/view/&quot; + str(q.id()))
</pre>
<p>Ved GET-etterspørsler vil get-metoden benyttes, og den returnerer en nettside. Ved POST-etterspørsel vil det bli opprettet et nytt objekt med informasjon, og ved hjelp av put() blir objektet lagret i datastore, for så å sende brukeren til en side som viser det nye objektet.</p>
<p>Det er viktig å merke seg at det ikke finnes noen mulighet for auto increment som man kjenner fra andre databaser, så id-feltet som ser ut til å ligge lagret i modellen inneholder None (null). For å simulere auto increment er det laget to metoder som tar var på den siste id-verdien i memcache.</p>
<pre class="brush: python;">
def getLast():
last = memcache.get(&quot;last&quot;)
if last:
return last
return setLast()

def setLast():
id = Quote.all().order(&quot;-id&quot;).get().id
memcache.set(&quot;last&quot;, id, 3600*24)
return id
</pre>
<p>Ved hjelp av setLast blir den siste verdien hentet ut og lagt inn i memcache, som kan beskrives som delt minne mellom instansene av applikasjonen (Google starter ikke bare én). getLast har som formål å returnere verdien fra memcache, men ser til at den blir satt opp den ikke er satt. For å fylle opp id på Quote-objektet må jeg i tillegg bytte ut put-metoden til modellen.</p>
<pre class="brush: python;">
def put(self):
self.id = memcache.incr(&quot;last&quot;)
if not self.id:
setLast()
self.id = memcache.incr(&quot;last&quot;)
return self.put()
</pre>
<p>Selv om jeg setter en id-verdi på objektet har objektet allerede en annen id som er tilgjengelig gjennom objekt.key().id(), og det er objektet fra objekt.key() som returneres ved put(). Den genererte id-en fra Google trenger følge på hverandre slik man gjerne opplever når man utvikler lokalt.</p>
<p>For å vise et gitt sitat er det ønskelig å benytte id-verdien som objektet automatisk har fått, og for å hente sitater ut gjør denne verdien det enkelt.</p>
<pre class="brush: python;">
class View(Base):
def get(self, id):
quote = Quote.get_by_id(int(id))
if not quote:
raise Exception(&quot;Requested quote doesn't exists&quot;)
self.render(&quot;view.html&quot;, dict(quote=quote))
</pre>
<p>Quote.get_by_id() er en metode som gjør det veldig greit å hente et gitt objekt uten å måtte skrive en hel spørring. Dersom objektet som etterspørres ikke eksisterer er det ønskelig å lage et avvik, dette blir tatt hånd om av en metode i Base-klassen som er laget for å slippe å skrive mye kode:</p>
<pre class="brush: python;">
class Base(webapp.RequestHandler):

def render(self, tpl, values = {}):
values[&quot;user&quot;] = users.get_current_user()
values[&quot;url_login&quot;] = users.create_login_url(self.request.url)
values[&quot;url_logout&quot;] = users.create_logout_url(&quot;/&quot;)

self.response.out.write(
template.render(&quot;template/&quot; + tpl, values)) # Lispy!

def handle_exception(self, exception, debug_mode):
self.response.out.write(
template.render(&quot;template/error.html&quot;, dict(error=exception)))
</pre>
<p>Denne klassen er laget kun for å slippe å skrive kode mange ganger (DRY). handle_excception tar imot avvik og lar meg håndtere disse på en fornuftig måte. render-metoden er en metode for å gjøre koden enklere, men også for å sikre at det blir sendt med noen ekstra variabler som link til innlogging og brukerinformasjon.</p>
<p>En av begrensningene til Datastore er at det mangler mulighet for å sortere i en tilfeldig rekkefølge (&#8220;SELECT &#8230; ORDER BY rand()&#8221;). Det er flere som har prøvd å løse denne mangelen, men siden jeg har laget meg en tilnærming til auto increment på tabellen må jeg klare å lage en tilfeldig verdi mellom den laveste og den høyeste mulige verdien.</p>
<pre class="brush: python;">
class Front(Base):
def get(self):
rnd = random.randint(getFirst(), getLast())
quote = Quote.all().filter(&quot;id &gt;= &quot;, rnd).order(&quot;id&quot;).get()
if not quote:
quote = Quote.all().filter(&quot;id &lt; &quot;, rnd).order(&quot;id&quot;).get()
self.render(&quot;front.html&quot;, dict(quote=quote))
</pre>
<p>Den er muligens noe omfattende, men det er en tilnærming for å sikre at man alltid finner minst et sitat (så lenge det er noen i tabellen).</p>
<p>For å fortelle hvilke klasser som skal benyttes ved kall mot serveren må applikasjonen vite om disse, dette gjøres ved å legge inn informasjon om dette i filen;</p>
<pre class="brush: python;">
application = webapp.WSGIApplication([
(&quot;/&quot;, Front),
(&quot;/add&quot;, Add),
(&quot;/view/(\d*)&quot;, View),
(&quot;.*&quot;, Error),
], debug=True)

def main():
run_wsgi_app(application)

if __name__ == &quot;__main__&quot;:
main()
</pre>
<p>I tillegg må det være med en app.yaml som forteller enda litt til om applikasjonen:</p>
<pre class="brush: python;">
application: rndquote
version: 1
runtime: python
api_version: 1

handlers:
- url: /style
static_dir: style

- url: /add
script: quote.py
login: required

- url: /admin.*
script: $PYTHON_LIB/google/appengine/ext/admin
login: admin

- url: /.*
script: quote.py
</pre>
<p>Det er verdt å merke seg at &#8220;login: required&#8221; gjør det at man må være innlogget for å få opp siden, hvilket er grunnen til at det ikke er noen kode for brukerhåndtering og annet i koden.</p>
<p>I tillegg til noen metoder og et par andre småting er applikasjonen ferdig, og den kan testes på <a href="http://rndquote.appspot.com/">rndquote.appspot.com</a>. All nødvendig <a href="http://blog.averlend.com/wp-content/uploads/2008/11/rndquote.zip">kildekode for å kjøre programmet</a> kan lastes ned (må ha SDK i tillegg).</p>
<p>GAE er ikke kommet i endelig utgave enda, og det er satt en del begrensninger (maks 650 000 kall mot datastore og 2 500 000 sidekall per døgn), i tillegg er det enkelte ting som ikke er der som man normalt forventer å ha tilgjengelig når man utvikler for veven.</p>
<p>Til tross for begrensninger er det ingen tvil om at dette er gøy å leke med, og det er ikke vanskelig å se for seg en del ting som egner seg å lage for GAE.</p>
]]></content:encoded>
			<wfw:commentRss>http://klakegg.net/2008/11/en-titt-pa-google-app-engine/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

