nosql-tutorial 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,95 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # 1. zakładamy bazę:
4
+ # createdb test
5
+ # 2. uruchamiamy ten skrypt:
6
+ # ruby 010-postgres.rb
7
+ # 3. przyłączamy się do bazy:
8
+ # psql test
9
+ # \d
10
+ # \encoding # powinno być UTF8
11
+ # \q
12
+
13
+ require 'rubygems'
14
+ require 'dm-core'
15
+
16
+ # http://cheat.errtheblog.com/s/datamapper/
17
+ # DataMapper.setup(:default, "adapter://user:password@hostname/dbname")
18
+
19
+ # sudo gem install dm-imap-adapter
20
+ # sudo gem install dm-yaml-adapter
21
+
22
+ # http://datamapper.rubyforge.org/dm-core/DataMapper.html
23
+ #
24
+ # DataMapper.setup(:default, {
25
+ # :adapter => 'adapter_name_here',
26
+ # :database => "path/to/repo",
27
+ # :username => 'username',
28
+ # :password => 'password',
29
+ # :host => 'hostname'
30
+ # })
31
+
32
+ # :fatal, :error, :warn, :info, :debug
33
+
34
+ log = DataMapper::Logger.new(STDOUT, :debug)
35
+
36
+ log.push "==== hello datamapper"
37
+
38
+ DataMapper.setup(:default,'postgres:test')
39
+
40
+ class Post
41
+ include DataMapper::Resource
42
+
43
+ property :id, Serial
44
+ property :title, String
45
+ property :body, Text
46
+ property :created_at, DateTime
47
+
48
+ has n, :comments
49
+ end
50
+
51
+ class Comment
52
+ include DataMapper::Resource
53
+
54
+ property :id, Serial
55
+ property :posted_by, String
56
+ property :email, String
57
+ property :url, String
58
+ property :body, Text
59
+
60
+ belongs_to :post
61
+ end
62
+
63
+ class Category
64
+ include DataMapper::Resource
65
+
66
+ property :id, Serial
67
+ property :name, String
68
+ end
69
+
70
+ class Categorization
71
+ include DataMapper::Resource
72
+
73
+ property :id, Serial
74
+ property :created_at, DateTime
75
+
76
+ belongs_to :category
77
+ belongs_to :post
78
+ end
79
+
80
+ class Post
81
+ has n, :categorizations
82
+ has n, :categories, :through => :categorizations
83
+ end
84
+
85
+ class Category
86
+ has n, :categorizations
87
+ has n, :posts, :through => :categorizations
88
+ end
89
+
90
+ # Post.auto_migrate!
91
+ # Category.auto_migrate!
92
+ # Comment.auto_migrate!
93
+ # Categorization.auto_migrate!
94
+
95
+ DataMapper.auto_migrate!
@@ -0,0 +1,25 @@
1
+ require 'rubygems'
2
+ require 'dm-core'
3
+ require 'extlib'
4
+
5
+ DataMapper.setup :default, 'sqlite3:test.sqlite3'
6
+
7
+ Extlib::Inflection.plural_word 'xs', 'xse'
8
+
9
+ class Xs
10
+ #class Comment
11
+ include DataMapper::Resource
12
+ property :id, Serial
13
+ property :name, String
14
+ belongs_to :film
15
+ end
16
+
17
+ class Film
18
+ include DataMapper::Resource
19
+ property :id, Serial
20
+ property :name, String
21
+ has n, :xse
22
+ #has n, :comments
23
+ end
24
+
25
+ DataMapper.auto_migrate!
@@ -0,0 +1,40 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'rubygems'
4
+ require 'action_mailer'
5
+ require 'yaml'
6
+
7
+ # wczytujemy login + hasło z pliku private.yaml
8
+ #
9
+ # ---
10
+ # sigma:
11
+ # :login: wbzyl
12
+ # :password: 'alamakota'
13
+ # julia:
14
+ # :login: matwb
15
+ # :password: 'razdwatrzy'
16
+
17
+ private = YAML.load(IO.read('../../../../../private.yaml'))
18
+
19
+ ActionMailer::Base.smtp_settings = {
20
+ :address => 'inf.ug.edu.pl',
21
+ :port => 25,
22
+ :domain => 'ug.edu.pl',
23
+ :user_name => private['sigma'][:login],
24
+ :password => private['sigma'][:password],
25
+ :authentication => :login
26
+ }
27
+
28
+ class Notification < ActionMailer::Base
29
+ def signup_message(recipient)
30
+ from 'wbzyl@inf.ug.edu.pl'
31
+ content_type 'text/plain; charset=utf-8'
32
+ recipients recipient
33
+ subject 'action mailer test: 4'
34
+ body 'Treść emaila #4.'
35
+ end
36
+ end
37
+
38
+ puts Notification.create_signup_message('matwb@ug.edu.pl')
39
+
40
+ Notification.deliver_signup_message('matwb@ug.edu.pl')
@@ -0,0 +1,62 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'rubygems'
4
+ require 'action_mailer'
5
+ require 'mime/types'
6
+ require 'yaml'
7
+
8
+ # wczytujemy login + hasło z pliku private.yaml
9
+ #
10
+ # ---
11
+ # sigma:
12
+ # :login: wbzyl
13
+ # :password: 'alamakota'
14
+ # julia:
15
+ # :login: matwb
16
+ # :password: 'razdwatrzy'
17
+
18
+ private = YAML.load(IO.read('../../../../../private.yaml'))
19
+
20
+ ActionMailer::Base.smtp_settings = {
21
+ :address => 'inf.ug.edu.pl',
22
+ :port => 25,
23
+ :domain => 'ug.edu.pl',
24
+ :user_name => private['sigma'][:login],
25
+ :password => private['sigma'][:password],
26
+ :authentication => :login
27
+ }
28
+
29
+ class Notification < ActionMailer::Base
30
+ def directory_dump(recipient, directory=Dir.pwd)
31
+ from 'wbzyl@inf.ug.edu.pl'
32
+ content_type 'text/plain; charset=utf-8'
33
+ recipients recipient
34
+ subject "zdjęcia z katalogu: #{directory}"
35
+ body %{Zdjęcia z katalogu: "#{directory}":}
36
+ Dir.glob('*.jpeg') do |f|
37
+ path = File.join(directory, f)
38
+ attachment('image/jpeg') do |a|
39
+ a.body = File.read(path)
40
+ a.filename = f
41
+ a.transfer_encoding = 'base64'
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ #puts Notification.create_directory_dump('matwb@ug.edu.pl', Dir.pwd)
48
+
49
+ Notification.deliver_directory_dump('matwb@ug.edu.pl')
50
+
51
+ __END__
52
+
53
+ removing file from repo:
54
+
55
+ git filter-branch --index-filter \
56
+ 'git rm --cached --ignore-unmatch private.yaml' merge-point..HEAD
57
+
58
+ # remove the temporary history git-filter-branch otherwise leaves behind for a long time
59
+ rm -rf .git/refs/original/ && git reflog expire --all && git gc --aggressive --prune
60
+
61
+ git push
62
+ #git push -f
@@ -0,0 +1,34 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ # sudo gem install ambethia-smtp-tls -v '1.1.2' --source http://gems.github.com
4
+
5
+ require 'rubygems'
6
+ require 'action_mailer'
7
+ require 'smtp-tls'
8
+
9
+ require 'yaml'
10
+
11
+ private = YAML.load(IO.read('../../../../../private.yaml'))['gmail']
12
+
13
+ ActionMailer::Base.delivery_method = :smtp
14
+
15
+ class SimpleMailer < ActionMailer::Base
16
+ def simple_message(recipient)
17
+ from 'wlodek.bzyl@gmail.com'
18
+ recipients recipient
19
+ subject 'Tę wiadomość wysłano z Gmail'
20
+ body 'To naprawdę działa!'
21
+ end
22
+ end
23
+
24
+ ActionMailer::Base.smtp_settings = {
25
+ :address => 'smtp.gmail.com',
26
+ :domain => 'gmail.com',
27
+ :port => 587,
28
+ :user_name => private['login'],
29
+ :password => private['password'],
30
+ :authentication => 'plain',
31
+ :enable_starttls_auto => true
32
+ }
33
+
34
+ SimpleMailer.deliver_simple_message('matwb@ug.edu.pl')
@@ -0,0 +1,7 @@
1
+ ---
2
+ sigma:
3
+ :login: wbzyl
4
+ :password: 'alamakota'
5
+ julia:
6
+ :login: matwb
7
+ :password: 'razdwatrzy'
@@ -110,6 +110,14 @@ blockquote p.author {
110
110
  text-align: right;
111
111
  }
112
112
 
113
+ blockquote h2 {
114
+ font-size: 100%;
115
+ font-family: Cyklop, sans-serif;
116
+ padding-bottom: 0px;
117
+ border-bottom: none;
118
+ color: #A00;
119
+ }
120
+
113
121
  h2 {
114
122
  font-size: 130%;
115
123
  padding-bottom: 6px;
@@ -0,0 +1,5 @@
1
+ #### {% title "Blogi" %}
2
+
3
+ # Lista obecności
4
+
5
+ Lista blogów.
@@ -0,0 +1,83 @@
1
+ #### {% title "CouchDB — CouchApp" %}
2
+
3
+ # CouchDB — CouchApp
4
+
5
+ Czym jest [CouchApp]():
6
+ „CouchApp—a set of scripts that allow complete, stand-alone CouchDB
7
+ applications to be built using just HTML and JavaScript. These
8
+ applications are housed in the CouchDB database, meaning that when the
9
+ database is replicated, any applications stored in that database are
10
+ also replicated.”
11
+
12
+ ## Instalacja
13
+
14
+ CouchApp jest modułem do Pythona. Moduły można instalować
15
+ korzystając z programu *easy_install*.
16
+ Program ten znajdziemy w paczce o nazwie *setuptool*
17
+ (albo o podobnej nazwie).
18
+
19
+ sudo yum install setuptool
20
+ sudo easy_install couchdb
21
+ sudo easy_install simplejson
22
+ sudo easy_install couchapp
23
+
24
+ ## Hello World
25
+
26
+ mkdir ~/couchapps
27
+ cd ~/couchapps
28
+ couchapp hello_world
29
+
30
+ Teraz
31
+
32
+ tree hello_world
33
+ hello_world/
34
+ |-- _attachments
35
+ | |-- index.html
36
+ | `-- style
37
+ | `-- main.css
38
+ |-- _id
39
+ |-- couchapp.json
40
+ |-- lists
41
+ |-- shows
42
+ |-- updates
43
+ |-- vendor
44
+ | `-- couchapp
45
+ | |-- README.md
46
+ | |-- _attachments
47
+ | | `-- jquery.couchapp.js
48
+ | |-- couchapp.js
49
+ | |-- date.js
50
+ | |-- metadata.json
51
+ | |-- path.js
52
+ | `-- template.js
53
+ `-- views
54
+
55
+ Następnie:
56
+
57
+ couchapp push hello_world http://127.0.0.1:5984/hello_world
58
+ [INFO] Visit your CouchApp here:
59
+ http://127.0.0.1:5984/hello_world/_design/hello_world/index.html
60
+
61
+ Wchodzimy na powyżej wypisany URI.
62
+
63
+ Wymieniamy zawartość pliku *index.html*:
64
+
65
+ <!DOCTYPE html>
66
+ <html>
67
+ <head>
68
+ <meta charset="utf-8">
69
+ <title>Witaj CouchApp</title>
70
+ <link rel="stylesheet" href="style/main.css" type="text/css">
71
+ </head>
72
+ <body>
73
+ <h1>Witaj CouchApp</h1>
74
+ <p>Jest fajnie!</p>
75
+ </body>
76
+ <script src="/_utils/script/json2.js"></script>
77
+ <script src="/_utils/script/jquery.js?1.3.2"></script>
78
+ <script src="/_utils/script/jquery.couch.js?0.11.0b"></script>
79
+ </html>
80
+
81
+ Uaktualniamy aplikację, wykonując z katalogu *hello_world* polecenie:
82
+
83
+ couchapp push . http://127.0.0.1:5984/hello_world
@@ -0,0 +1,205 @@
1
+ #### {% title "CouchDB – CRUD" %}
2
+
3
+ # CouchDB – Dokumenty
4
+
5
+ W przykładach poniżej będziemy korzystać z:
6
+
7
+ * [HTTP Document API](http://wiki.apache.org/couchdb/HTTP_Document_API)
8
+
9
+
10
+ ## Wykonywanie CRUD na dokumentach
11
+
12
+ Zaczynamy od utworzenia bazy o nazwie *owoce*:
13
+
14
+ curl -X PUT http://127.0.0.1:5984/owoce/
15
+
16
+ ### Create
17
+
18
+ Dodajemy kilka rekordów do bazy korzystając z programu *curl*:
19
+
20
+ curl -X PUT http://127.0.0.1:5984/owoce/1001 \
21
+ -d '{"item":"jabłko","price":{"biedronka":3.44,"tesco":2.12,"real":4.10}}'
22
+ curl -X PUT http://127.0.0.1:5984/owoce/1002 \
23
+ -d '{"item":"banan","price":{"biedronka":6.44,"tesco":1.12,"real":4.50}}'
24
+ curl -X PUT http://127.0.0.1:5984/owoce/1003 \
25
+ -d '{"item":"kiwi","price":{"biedronka":1.44,"tesco":1.12,"real":1.10}}'
26
+ curl -X PUT http://127.0.0.1:5984/owoce/1004 \
27
+ -d '{"item":"kumkwat"}'
28
+
29
+ Dodawanie rekordów do bazy w ten sposób jest trochę męczące.
30
+ [HTTP Bulk Document API](http://wiki.apache.org/couchdb/HTTP_Bulk_Document_API)
31
+ ułatwia wprowadzanie (usuwanie, uaktualnianie – też) wielu rekordów do bazy:
32
+
33
+ curl -X POST -d @owoce.json http://127.0.0.1:5984/owoce/_bulk_docs
34
+
35
+ gdzie plik *owoce.json* zawiera:
36
+
37
+ :::json
38
+ {
39
+ "docs": [
40
+ {"_id":"2001", "item":"gruszka", "price":{"tesco":5.00, "real":4.10}},
41
+ {"_id":"2002", "item":"wiśnia", "price":{"tesco":3.14, "real":2.71}},
42
+ {"_id":"2003", "item":"morela", "price":{"tesco":4.28, "real":4.10}}
43
+ ]
44
+ }
45
+
46
+ Jaką funkcję spełniają liczby 1001, …, 1004, 2001, 2002, 2003.
47
+ Czy można by było zamiast tych liczb użyć napisów: apple, banana, itd.?
48
+
49
+ curl -X PUT http://127.0.0.1:5984/owoce/orange -d '{"item":"pomarańcze"}'
50
+ => {"ok":true,"id":"orange","rev":"1-512c"}
51
+
52
+ Pole **rev**. Do czego ono służy?
53
+
54
+
55
+ ### Delete
56
+
57
+ Usuwamy ten dokument z bazy:
58
+
59
+ curl -X DELETE http://127.0.0.1:5984/owoce/orange?rev=1-2618
60
+ => {"ok":true,"id":"orange","rev":"1-123d"}
61
+
62
+
63
+ ### Get
64
+
65
+ Pobieramy dokument z bazy *owoce*:
66
+
67
+ curl -X GET http://127.0.0.1:5984/owoce/1001
68
+ => { "_id":"1001",
69
+ "_rev":"1-627c94",
70
+ "item":"jab\u0142ko","price":{"biedronka":3.44,"tesco":2.12,"real":4.1}
71
+ }
72
+
73
+
74
+ ### Copy (tego nie ma w CRUD)
75
+
76
+ Kopiujemy dokument z *_id = 1004*:
77
+
78
+ curl -X COPY http://127.0.0.1:5984/owoce/1004 -H "Destination: orange"
79
+ => {"id":"orange","rev":"1-46050e"}
80
+
81
+ Do czego służy opcja `-H`? Wskazówka: skorzystać z opcji `-v` (ang. *verbose*).
82
+
83
+ *Uwaga:* klucz **rev** wypisany powyżej jest przykładowy.
84
+
85
+
86
+ ### Update
87
+
88
+ Uaktualniamy dokument *orange*:
89
+
90
+ curl -X PUT http://127.0.0.1:5984/owoce/orange \
91
+ -d '{"_rev":"1-46050e3","item":"orange"}'
92
+
93
+ *Uwaga:* powyżej należy wpisać klucz **rev** wypisany przez polecenie *COPY*.
94
+
95
+
96
+ ## Wbudowane widoki
97
+
98
+ Jaki widok? Tak naprawdę, to zadajemy zapytanie.
99
+
100
+ Do pobrania wszystkich dokumentów z bazy *owoce* skorzystamy
101
+ z wbudowanego widoku *_all_docs*:
102
+
103
+ curl -X GET http://127.0.0.1:5984/owoce/_all_docs
104
+ => {"total_rows":6,"offset":0,"rows":[
105
+ {"id":"1001","key":"1001","value":{"rev":"1-627c"}},
106
+ ...
107
+ {"id":"orange","key":"orange","value":{"rev":"2-9014"}},
108
+ ]}
109
+
110
+ Można też zmienić kolejność pobieranych dokumentów:
111
+
112
+ curl -X GET http://127.0.0.1:5984/owoce/_all_docs?descending=true
113
+
114
+ Można też pobrać kilka dokumentów, np. tylko jeden dokument:
115
+
116
+ curl -X GET http://127.0.0.1:5984/owoce/_all_docs?descending=true\&limit=1
117
+
118
+ Można pobrać dokumenty z zawartością:
119
+
120
+ curl -X GET http://127.0.0.1:5984/owoce/_all_docs?include_docs=true
121
+
122
+ I jeszcze kilka przykładów (bazę *collator* tworzymy za pomocą
123
+ skryptu {%= link_to "collseq.rb", "/doc/couchdb/collseq.rb" %}):
124
+
125
+ curl -X GET http://localhost:5984/collator/_all_docs?startkey=\"64\"\&limit=4
126
+ curl -X GET http://localhost:5984/collator/_all_docs?startkey=\"64\"\&limit=2\&descending=true
127
+ curl -X GET http://localhost:5984/collator/_all_docs?startkey=\"64\"\&endkey=\"68\"
128
+
129
+ Skorzystamy jeszcze z jednego wbudowanego widoku:
130
+
131
+ curl -X GET http://127.0.0.1:5984/collator/_all_docs_by_seq
132
+
133
+ (Został usunięty w ostatniej wersji CouchDB?)
134
+
135
+
136
+ ## Załączniki
137
+
138
+ Dodajemy obrazek *kumkwat.jpg* do pierwszego rekordu z *_id=2001*:
139
+
140
+ curl -vX PUT http://127.0.0.1:5984/owoce/2001/kumkwat.jpg?rev=1-3854 \
141
+ -H "Content-Type: image/jpg" --data-binary @kumkwat.jpg
142
+
143
+ Obrazek pobieramy tak:
144
+
145
+ curl -X GET http://127.0.0.1:5984/owoce/2001/kumkwat.jpg > k.jpg
146
+
147
+
148
+ ## Przykłady
149
+
150
+ Pobieramy obrazek z bazy i wyświetlamy go na stronie:
151
+
152
+ :::html
153
+ <!DOCTYPE html>
154
+ <meta charset="utf-8" />
155
+ <title>Kumkwaty</title>
156
+ <p>
157
+ Kumkwaty są smaczne:
158
+ <img src="http://localhost:5984/owoce/2001/kumkwat.jpg">
159
+ </p>
160
+
161
+ Zaczynamy od omówienia przykładu
162
+ [jQuery.getJSON() – jQuery API](http://api.jquery.com/jQuery.getJSON/)
163
+
164
+ Teraz drugi przykład korzystający z
165
+ CouchDB + jQuery + JSONP oraz owocowa baza danych:
166
+
167
+ :::html
168
+ <!DOCTYPE html>
169
+ <html>
170
+ <head>
171
+ <meta charset="utf-8">
172
+ <title>JSONP</title>
173
+ </head>
174
+ <body>
175
+
176
+ <h4 id="item"></h4>
177
+ <p id="price"></p>
178
+
179
+ <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.1/jquery.min.js"></script>
180
+ <script>
181
+ $.getJSON('http://localhost:5984/owoce/2002', "callback=?", function(data){
182
+ $("#item").html(data.item);
183
+ $("#price").html("tesco: " + data.price["tesco"] + " real: " + data.price["real"]);
184
+ //console.log(data);
185
+ });
186
+ </script>
187
+
188
+ </body>
189
+ </html>
190
+
191
+ Powyższy kod umieszczamy w pliku *owoce.html* a sam plik w katalogu
192
+ *public_html* i sprawdzamy czy wszystko działa wchodząc na stronę:
193
+
194
+ http://localhost/~wbzyl/owoce.html
195
+
196
+ Czy zadziała, jeśli plik *owoce.html* umieścimy na *Sigmie*
197
+ i wejdziemy na stronę:
198
+
199
+ http://sigma.ug.edu.pl/~wbzyl/owoce.html
200
+
201
+ Dlaczego? A jeśli zmienimy URI przy `$.getJSON`?
202
+
203
+
204
+ [couchdb]: http://books.couchdb.org/relax/ "CouchDB: The Definitive Guide"
205
+ [couchdb wiki]: http://wiki.apache.org/couchdb/ "Couchdb Wiki"