lokka 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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