nosql-tutorial 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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"