lokka 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.
Files changed (197) hide show
  1. data/Gemfile +48 -0
  2. data/LICENSE +20 -0
  3. data/README.ja.rdoc +46 -0
  4. data/README.rdoc +46 -0
  5. data/Rakefile +116 -0
  6. data/VERSION +1 -0
  7. data/bin/autotest +16 -0
  8. data/bin/bluefeather +16 -0
  9. data/bin/convert_to_should_syntax +16 -0
  10. data/bin/css2sass +16 -0
  11. data/bin/edit_json.rb +16 -0
  12. data/bin/erubis +16 -0
  13. data/bin/exceptional +16 -0
  14. data/bin/haml +16 -0
  15. data/bin/html2haml +16 -0
  16. data/bin/jeweler +16 -0
  17. data/bin/lokka +4 -0
  18. data/bin/prettify_json.rb +16 -0
  19. data/bin/rackup +16 -0
  20. data/bin/rake +16 -0
  21. data/bin/rcov +16 -0
  22. data/bin/sass +16 -0
  23. data/bin/sass-convert +16 -0
  24. data/bin/tilt +16 -0
  25. data/bin/unit_diff +16 -0
  26. data/config.ru +3 -0
  27. data/config.yml +4 -0
  28. data/i18n/en.yml +149 -0
  29. data/i18n/ja.yml +149 -0
  30. data/init.rb +7 -0
  31. data/install.rb +16 -0
  32. data/lib/lokka.rb +138 -0
  33. data/lib/lokka/app.rb +562 -0
  34. data/lib/lokka/before.rb +29 -0
  35. data/lib/lokka/bread_crumb.rb +34 -0
  36. data/lib/lokka/category.rb +33 -0
  37. data/lib/lokka/comment.rb +26 -0
  38. data/lib/lokka/entry.rb +70 -0
  39. data/lib/lokka/helpers.rb +214 -0
  40. data/lib/lokka/option.rb +23 -0
  41. data/lib/lokka/site.rb +19 -0
  42. data/lib/lokka/tag.rb +5 -0
  43. data/lib/lokka/theme.rb +17 -0
  44. data/lib/lokka/user.rb +77 -0
  45. data/lib/sqlite3.dll +0 -0
  46. data/lokka.exe +0 -0
  47. data/lokka.exy +15 -0
  48. data/lokka.gemspec +373 -0
  49. data/lokka.ico +0 -0
  50. data/lokka.rb +11 -0
  51. data/public/admin/categories/edit.haml +6 -0
  52. data/public/admin/categories/form.haml +18 -0
  53. data/public/admin/categories/index.haml +15 -0
  54. data/public/admin/categories/new.haml +5 -0
  55. data/public/admin/comments/edit.haml +6 -0
  56. data/public/admin/comments/form.haml +29 -0
  57. data/public/admin/comments/index.haml +22 -0
  58. data/public/admin/comments/new.haml +5 -0
  59. data/public/admin/css/editor.css +10 -0
  60. data/public/admin/css/jquery.cleditor.css +24 -0
  61. data/public/admin/css/style.css +710 -0
  62. data/public/admin/edit.haml +42 -0
  63. data/public/admin/favicon.ico +0 -0
  64. data/public/admin/images/add.png +0 -0
  65. data/public/admin/images/aside_arrow.png +0 -0
  66. data/public/admin/images/buttons.gif +0 -0
  67. data/public/admin/images/category.png +0 -0
  68. data/public/admin/images/comment.png +0 -0
  69. data/public/admin/images/dashboard.png +0 -0
  70. data/public/admin/images/file.png +0 -0
  71. data/public/admin/images/files.png +0 -0
  72. data/public/admin/images/mail-attachment.png +0 -0
  73. data/public/admin/images/plugin.png +0 -0
  74. data/public/admin/images/post.png +0 -0
  75. data/public/admin/images/setting.png +0 -0
  76. data/public/admin/images/tag.png +0 -0
  77. data/public/admin/images/theme.png +0 -0
  78. data/public/admin/images/toolbar.gif +0 -0
  79. data/public/admin/images/user.png +0 -0
  80. data/public/admin/index.haml +1 -0
  81. data/public/admin/js/editor.js +7 -0
  82. data/public/admin/js/jquery.cleditor.js +1132 -0
  83. data/public/admin/js/jquery.cleditor.min.js +31 -0
  84. data/public/admin/layout.haml +91 -0
  85. data/public/admin/pages/edit.haml +6 -0
  86. data/public/admin/pages/form.haml +33 -0
  87. data/public/admin/pages/index.haml +23 -0
  88. data/public/admin/pages/new.haml +5 -0
  89. data/public/admin/plugins/index.haml +4 -0
  90. data/public/admin/posts/edit.haml +6 -0
  91. data/public/admin/posts/form.haml +33 -0
  92. data/public/admin/posts/index.haml +23 -0
  93. data/public/admin/posts/new.haml +5 -0
  94. data/public/admin/show.haml +7 -0
  95. data/public/admin/signup.haml +17 -0
  96. data/public/admin/site/edit.haml +13 -0
  97. data/public/admin/tags/edit.haml +6 -0
  98. data/public/admin/tags/form.haml +10 -0
  99. data/public/admin/tags/index.haml +19 -0
  100. data/public/admin/themes/index.haml +13 -0
  101. data/public/admin/users/edit.haml +6 -0
  102. data/public/admin/users/form.haml +22 -0
  103. data/public/admin/users/index.haml +24 -0
  104. data/public/admin/users/new.haml +5 -0
  105. data/public/plugin/lokka-google_analytics/lib/lokka/google_analytics.rb +29 -0
  106. data/public/plugin/lokka-google_analytics/views/index.haml +15 -0
  107. data/public/plugin/lokka-hello/lib/lokka/hello.rb +15 -0
  108. data/public/plugin/lokka-markdown/lib/lokka/markdown.rb +12 -0
  109. data/public/plugin/lokka-rbconfig/lib/lokka/rbconfig.rb +11 -0
  110. data/public/plugin/lokka-rbconfig/views/index.haml +7 -0
  111. data/public/plugin/lokka-rbconfig/views/style.css +12 -0
  112. data/public/system/404.haml +20 -0
  113. data/public/system/500.haml +19 -0
  114. data/public/system/comments/form.haml +22 -0
  115. data/public/system/favicon.ico +0 -0
  116. data/public/system/index.builder +25 -0
  117. data/public/system/style.css +8 -0
  118. data/public/theme/default/entries.erb +40 -0
  119. data/public/theme/default/entry.erb +23 -0
  120. data/public/theme/default/layout.erb +74 -0
  121. data/public/theme/default/quote.gif +0 -0
  122. data/public/theme/default/screenshot.png +0 -0
  123. data/public/theme/default/style.css +1147 -0
  124. data/public/theme/jarvi/entries.erb +26 -0
  125. data/public/theme/jarvi/entry.erb +14 -0
  126. data/public/theme/jarvi/favicon.ico +0 -0
  127. data/public/theme/jarvi/images/aside_dt.gif +0 -0
  128. data/public/theme/jarvi/images/aside_dt.png +0 -0
  129. data/public/theme/jarvi/images/footer.gif +0 -0
  130. data/public/theme/jarvi/images/footer.psd +0 -0
  131. data/public/theme/jarvi/images/header_deascription_ul.gif +0 -0
  132. data/public/theme/jarvi/images/header_language.gif +0 -0
  133. data/public/theme/jarvi/images/header_nav.gif +0 -0
  134. data/public/theme/jarvi/images/html.gif +0 -0
  135. data/public/theme/jarvi/images/index_content.gif +0 -0
  136. data/public/theme/jarvi/images/index_content_footer.gif +0 -0
  137. data/public/theme/jarvi/images/index_content_header.gif +0 -0
  138. data/public/theme/jarvi/images/index_header_nav.gif +0 -0
  139. data/public/theme/jarvi/images/section_header_title.gif +0 -0
  140. data/public/theme/jarvi/images/wide_content.gif +0 -0
  141. data/public/theme/jarvi/images/wide_content_body.gif +0 -0
  142. data/public/theme/jarvi/images/wide_content_h3.gif +0 -0
  143. data/public/theme/jarvi/layout.erb +55 -0
  144. data/public/theme/jarvi/screenshot.png +0 -0
  145. data/public/theme/jarvi/style.css +618 -0
  146. data/public/theme/lokka-org/article.haml +9 -0
  147. data/public/theme/lokka-org/entries.haml +9 -0
  148. data/public/theme/lokka-org/entry.haml +1 -0
  149. data/public/theme/lokka-org/favicon.ico +0 -0
  150. data/public/theme/lokka-org/images/aside_dt.gif +0 -0
  151. data/public/theme/lokka-org/images/aside_dt.png +0 -0
  152. data/public/theme/lokka-org/images/download_button.jpg +0 -0
  153. data/public/theme/lokka-org/images/footer.gif +0 -0
  154. data/public/theme/lokka-org/images/footer.psd +0 -0
  155. data/public/theme/lokka-org/images/header_capture.gif +0 -0
  156. data/public/theme/lokka-org/images/header_capture.jpg +0 -0
  157. data/public/theme/lokka-org/images/header_capture_a.gif +0 -0
  158. data/public/theme/lokka-org/images/header_deascription_ul.gif +0 -0
  159. data/public/theme/lokka-org/images/header_h1_a.gif +0 -0
  160. data/public/theme/lokka-org/images/header_language.gif +0 -0
  161. data/public/theme/lokka-org/images/header_nav.gif +0 -0
  162. data/public/theme/lokka-org/images/heder_nav_home.gif +0 -0
  163. data/public/theme/lokka-org/images/heder_nav_home.jpg +0 -0
  164. data/public/theme/lokka-org/images/html.gif +0 -0
  165. data/public/theme/lokka-org/images/index_content.gif +0 -0
  166. data/public/theme/lokka-org/images/index_content_footer.gif +0 -0
  167. data/public/theme/lokka-org/images/index_content_header.gif +0 -0
  168. data/public/theme/lokka-org/images/index_header_h1.gif +0 -0
  169. data/public/theme/lokka-org/images/index_header_nav.gif +0 -0
  170. data/public/theme/lokka-org/images/language_a.png +0 -0
  171. data/public/theme/lokka-org/images/section_header_title.gif +0 -0
  172. data/public/theme/lokka-org/images/wide_content.gif +0 -0
  173. data/public/theme/lokka-org/images/wide_content_body.gif +0 -0
  174. data/public/theme/lokka-org/images/wide_content_h3.gif +0 -0
  175. data/public/theme/lokka-org/index.haml +5 -0
  176. data/public/theme/lokka-org/layout.haml +72 -0
  177. data/public/theme/lokka-org/quote.gif +0 -0
  178. data/public/theme/lokka-org/screenshot.png +0 -0
  179. data/public/theme/lokka-org/style.css +806 -0
  180. data/public/theme/p0t/article.haml +10 -0
  181. data/public/theme/p0t/entries.haml +9 -0
  182. data/public/theme/p0t/entry.haml +7 -0
  183. data/public/theme/p0t/favicon.ico +0 -0
  184. data/public/theme/p0t/images/quote.gif +0 -0
  185. data/public/theme/p0t/layout.haml +68 -0
  186. data/public/theme/p0t/screenshot.png +0 -0
  187. data/public/theme/p0t/style.css +359 -0
  188. data/public/theme/vicuna-mono/entries.erb +40 -0
  189. data/public/theme/vicuna-mono/entry.erb +23 -0
  190. data/public/theme/vicuna-mono/layout.erb +76 -0
  191. data/public/theme/vicuna-mono/quote.gif +0 -0
  192. data/public/theme/vicuna-mono/screenshot.png +0 -0
  193. data/public/theme/vicuna-mono/style.css +1156 -0
  194. data/test/helper.rb +23 -0
  195. data/test/lokka/app_test.rb +15 -0
  196. data/test/lokka/post_test.rb +17 -0
  197. metadata +965 -0
@@ -0,0 +1,29 @@
1
+ module Lokka
2
+ module Before
3
+ def self.registered(app)
4
+ app.before do
5
+ begin
6
+ Site.first
7
+ rescue DataObjects::SyntaxError
8
+ Lokka::Database.new.create.setup
9
+ end
10
+ end
11
+
12
+ app.before do
13
+ @site = Site.first
14
+ @title = @site.title
15
+ @theme = Theme.new(settings.theme)
16
+ @theme_types = []
17
+ session[:locale] = params[:locale] if params[:locale]
18
+ end
19
+
20
+ app.before %r{(?!^/admin/login$)^/admin/.*$} do
21
+ login_required
22
+ end
23
+
24
+ app.before %r{(?!^/admin/login$)^/admin/.*$} do
25
+ login_required
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,34 @@
1
+ class Bread
2
+ attr_accessor :name, :link, :last
3
+
4
+ def initialize(name, link, last = false)
5
+ @name = name
6
+ @link = link
7
+ @last = last
8
+ end
9
+
10
+ def last?
11
+ @last
12
+ end
13
+ end
14
+
15
+ class BreadCrumb
16
+ include Enumerable
17
+ attr_accessor :breads
18
+
19
+ def initialize(breads = [])
20
+ @breads = breads
21
+
22
+ end
23
+
24
+ def add(name, link)
25
+ @breads << Bread.new(name, link)
26
+ end
27
+
28
+ def each
29
+ @breads.last.last = true
30
+ @breads.each do |bread|
31
+ yield bread
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,33 @@
1
+ class Category
2
+ include DataMapper::Resource
3
+
4
+ property :id, Serial
5
+ property :slug, Slug, :length => 255, :unique => true
6
+ property :title, String, :length => 255, :unique => true
7
+ property :description, Text
8
+ property :type, Discriminator
9
+ property :created_at, DateTime
10
+ property :updated_at, DateTime
11
+ property :parent_id, Integer
12
+
13
+ is :tree, :order => :title
14
+
15
+ has n, :entries
16
+
17
+ def self.get_by_fuzzy_slug(str)
18
+ ret = first(:slug => str)
19
+ ret.blank? ? get(str) : ret
20
+ end
21
+
22
+ def fuzzy_slug
23
+ slug.blank? ? id : slug
24
+ end
25
+
26
+ def link
27
+ cats = [fuzzy_slug]
28
+ ancestors.each do |ancestor|
29
+ cats.unshift ancestor.fuzzy_slug
30
+ end
31
+ "/category/#{cats.join('/')}/"
32
+ end
33
+ end
@@ -0,0 +1,26 @@
1
+ class Comment
2
+ include DataMapper::Resource
3
+
4
+ property :id, Serial
5
+ property :entry_id, Integer
6
+ property :name, String
7
+ property :homepage, String
8
+ property :body, Text
9
+ property :created_at, DateTime
10
+ property :updated_at, DateTime
11
+
12
+ belongs_to :entry
13
+
14
+ default_scope(:default).update(:order => :created_at.desc)
15
+
16
+ validates_presence_of :name
17
+ validates_presence_of :body
18
+
19
+ def self.recent(count = 5)
20
+ all(:limit => count, :order => [:created_at.desc])
21
+ end
22
+
23
+ def link
24
+ "#{self.entry.link}#comment-#{id}"
25
+ end
26
+ end
@@ -0,0 +1,70 @@
1
+ # coding: utf-8
2
+ class Entry
3
+ include DataMapper::Resource
4
+
5
+ property :id, Serial
6
+ property :user_id, Integer
7
+ property :category_id, Integer
8
+ property :slug, Slug, :length => 255
9
+ property :title, String, :length => 255
10
+ property :body, Text
11
+ property :type, Discriminator
12
+ property :created_at, DateTime
13
+ property :updated_at, DateTime
14
+
15
+ belongs_to :user
16
+ belongs_to :category, :required => false
17
+ has n, :comments
18
+
19
+ has_tags
20
+
21
+ default_scope(:default).update(:order => [:created_at.desc])
22
+
23
+ validates_presence_of :title
24
+ validates_uniqueness_of :slug
25
+ validates_uniqueness_of :title
26
+
27
+ def tag_collection=(string)
28
+ reg = RUBY_VERSION >= "1.9.0" ? /[^\p{Word}_]/i : /[^\w\s_-]/i
29
+ @tag_list = string.to_s.split(',').map { |name|
30
+ name.force_encoding(Encoding.default_external).gsub(reg, '').strip
31
+ }.reject{|x|x.blank?}.uniq.sort
32
+ end
33
+
34
+ def self.get_by_fuzzy_slug(str)
35
+ ret = first(:slug => str)
36
+ ret.blank? ? get(str) : ret
37
+ end
38
+
39
+ def self.search(str)
40
+ all(:title.like => "%#{str}%") |
41
+ all(:body.like => "%#{str}%")
42
+ end
43
+
44
+ def self.recent(count = 5)
45
+ all(:limit => count, :order => [:created_at.desc])
46
+ end
47
+
48
+ def fuzzy_slug
49
+ slug.blank? ? id : slug
50
+ end
51
+
52
+ def link
53
+ "/#{fuzzy_slug}"
54
+ end
55
+
56
+ def edit_link
57
+ "/admin/#{self.class.to_s.tableize}/#{id}/edit"
58
+ end
59
+
60
+ def tags_to_html
61
+ html = '<ul>'
62
+ tags.each do |tag|
63
+ html += %Q(<li><a href="#{tag.link}">#{tag.name}</a></li>)
64
+ end
65
+ html + '</ul>'
66
+ end
67
+ end
68
+
69
+ class Post < Entry; end
70
+ class Page < Entry; end
@@ -0,0 +1,214 @@
1
+ module Lokka
2
+ module Helpers
3
+ include Rack::Utils
4
+
5
+ alias :h :escape_html
6
+
7
+ def index?; @theme_types.include?(:index); end
8
+ def search?; @theme_types.include?(:search); end
9
+ def category?; @theme_types.include?(:category); end
10
+ def tag?; @theme_types.include?(:tag); end
11
+ def yearly?; @theme_types.include?(:yearly); end
12
+ def monthly?; @theme_types.include?(:monthly); end
13
+ def daily?; @theme_types.include?(:daily); end
14
+ def entry?; @theme_types.include?(:entry); end
15
+ def entries?; @theme_types.include?(:entries); end
16
+
17
+ # h + n2br
18
+ def hbr(str)
19
+ h(str).gsub(/\r\n|\r|\n/, "<br />\n")
20
+ end
21
+
22
+ def login_required
23
+ if current_user.class != GuestUser
24
+ return true
25
+ else
26
+ session[:return_to] = request.fullpath
27
+ redirect '/admin/login'
28
+ return false
29
+ end
30
+ end
31
+
32
+ def current_user
33
+ if session[:user]
34
+ User.get(session[:user])
35
+ else
36
+ GuestUser.new
37
+ end
38
+ end
39
+
40
+ def logged_in?
41
+ !!session[:user]
42
+ end
43
+
44
+ def bread_crumb
45
+ html = '<ol>'
46
+ @bread_crumbs.each do |bread|
47
+ html += '<li>'
48
+ if bread.last?
49
+ html += bread.name
50
+ else
51
+ html += "<a href=\"#{bread.link}\">#{bread.name}</a>"
52
+ end
53
+ html += '</li>'
54
+ end
55
+ html += '</ol>'
56
+ html
57
+ end
58
+
59
+ def category_tree(categories = Category.roots)
60
+ html = '<ul>'
61
+ categories.each do |category|
62
+ html += '<li>'
63
+ html += "<a href=\"#{category.link}\">#{category.title}</a>"
64
+ if category.children.count > 0
65
+ html += category_tree(category.children)
66
+ end
67
+ html += '</li>'
68
+ end
69
+ html += '</ul>'
70
+ html
71
+ end
72
+
73
+ def render_detect(*names)
74
+ ret = ''
75
+ names.each do |name|
76
+ out = render_any(name)
77
+ unless out.blank?
78
+ ret = out
79
+ break
80
+ end
81
+ end
82
+
83
+ if ret.blank?
84
+ raise Lokka::NoTemplateError, "Template not found. #{[names.join(', ')]}"
85
+ else
86
+ ret
87
+ end
88
+ end
89
+
90
+ def partial(name, options = {})
91
+ options[:layout] = false
92
+ render_any(name, options)
93
+ end
94
+
95
+ def render_any(name, options = {})
96
+ ret = ''
97
+ settings.supported_templates.each do |ext|
98
+ out = rendering(ext, name, options)
99
+ out.force_encoding(Encoding.default_external) unless out.nil?
100
+ unless out.blank?
101
+ ret = out
102
+ break
103
+ end
104
+ end
105
+ ret
106
+ end
107
+
108
+ def rendering(ext, name, options = {})
109
+ locals = options[:locals] ? {:locals => options[:locals]} : {}
110
+ dir = request.path_info =~ %r{^/admin/.*} ? 'admin' : "theme/#{@theme.name}"
111
+ layout = "#{dir}/layout"
112
+ path = "#{dir}/#{name}"
113
+
114
+ puts "ext, name, theme, dir, file: #{ext}, #{name}, #{@theme.name}, #{dir}, #{settings.views}/#{path}.#{ext}"
115
+
116
+ if File.exist?("#{settings.views}/#{layout}.#{ext}")
117
+ options[:layout] = layout.to_sym if options[:layout].nil?
118
+ end
119
+ if File.exist?("#{settings.views}/#{path}.#{ext}")
120
+ send(ext.to_sym, path.to_sym, options, locals)
121
+ end
122
+ end
123
+
124
+ def comment_form
125
+ haml :'system/comments/form', :layout => false
126
+ end
127
+
128
+ def link_to_if(cond, name, url, options = {})
129
+ cond ? link_to(name, url, options) : name
130
+ end
131
+
132
+ def link_to_unless(cond, name, url, options = {})
133
+ link_to_if !cond, name, url, options
134
+ end
135
+
136
+ def link_to(name, url, options = {})
137
+ attrs = {:href => url}
138
+ if options[:confirm] and options[:method]
139
+ attrs[:onclick] = "if(confirm('#{options[:confirm]}')){var f = document.createElement('form');f.style.display = 'none';this.parentNode.appendChild(f);f.method = 'POST';f.action = this.href;var m = document.createElement('input');m.setAttribute('type', 'hidden');m.setAttribute('name', '_method');m.setAttribute('value', '#{options[:method]}');f.appendChild(m);f.submit();};return false"
140
+ end
141
+
142
+ options.delete :confirm
143
+ options.delete :method
144
+
145
+ attrs.update(options)
146
+
147
+ str = ''
148
+ attrs.each do |key, value|
149
+ str += %Q(#{key.to_s}="#{value}")
150
+ end
151
+
152
+ %Q(<a #{str}>#{name}</a>)
153
+ end
154
+
155
+ def select_field(object, method, values = [], options = {})
156
+ name = "#{object.class.name.downcase}[#{method}]"
157
+ v = object.send(method)
158
+
159
+ attrs = ''
160
+ options.each do |key, value|
161
+ attrs += %Q( #{key}="#{value}")
162
+ end
163
+
164
+ html = %Q(<select name="#{name}"#{attrs}>)
165
+ values.each do |value|
166
+ padding = value[0] == v ? ' selected="selected"' : ''
167
+ html += %Q(<option value="#{value[0]}"#{padding}>#{value[1]}</option>)
168
+ end
169
+ html + '</select>'
170
+ end
171
+
172
+ def truncate(text, options = {})
173
+ options = {:length => 30, :ommision => '...'}.merge(options)
174
+ if options[:length] < text.length
175
+ text[0..options[:length]] + options[:ommision]
176
+ else
177
+ text
178
+ end
179
+ end
180
+
181
+ def strip_tags(text)
182
+ text.gsub(/<.+?>/, '')
183
+ end
184
+
185
+ def months
186
+ ms = {}
187
+ Post.all.each do |post|
188
+ m = post.created_at.strftime('%Y-%m')
189
+ if ms[m].nil?
190
+ ms[m] = 1
191
+ else
192
+ ms[m] += 1
193
+ end
194
+ end
195
+
196
+ months = []
197
+ ms.each do |m, count|
198
+ year, month = m.split('-')
199
+ months << OpenStruct.new({:year => year, :month => month, :count => count})
200
+ end
201
+ months.sort {|x, y| y.year + y.month <=> x.year + x.month }
202
+ end
203
+
204
+ def header
205
+ s = yield_content :header
206
+ s unless s.blank?
207
+ end
208
+
209
+ def footer
210
+ s = yield_content :footer
211
+ s unless s.blank?
212
+ end
213
+ end
214
+ end
@@ -0,0 +1,23 @@
1
+ class Option
2
+ include DataMapper::Resource
3
+
4
+ property :name, String, :length => 255, :key => true
5
+ property :value, Text
6
+ property :created_at, DateTime
7
+ property :updated_at, DateTime
8
+
9
+ validates_presence_of :name
10
+
11
+ def self.method_missing(method, *args)
12
+ attribute = method.to_s
13
+ if attribute =~ /=$/
14
+ column = attribute[0, attribute.size - 1]
15
+ o = self.first_or_new(:name => column)
16
+ o.value = args.first
17
+ o.save
18
+ else
19
+ o = self.first_or_new(:name => method)
20
+ o.value
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,19 @@
1
+ class Site
2
+ include DataMapper::Resource
3
+
4
+ property :id, Serial
5
+ property :title, String, :length => 255
6
+ property :description, String, :length => 255
7
+ property :theme, String, :length => 64
8
+ property :created_at, DateTime
9
+ property :updated_at, DateTime
10
+
11
+ def method_missing(method, *args)
12
+ if method.to_s =~ /=$/
13
+ super
14
+ else
15
+ o = Option.first_or_new(:name => method)
16
+ o.value
17
+ end
18
+ end
19
+ end