kiosk 0.0.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,9 @@
1
+ module Kiosk
2
+ module ClaimedNode
3
+ attr_accessor :resource
4
+
5
+ def uri_attribute
6
+ (name = ['href', 'src'].detect { |attr| attributes.key?(attr) }) && attributes[name]
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,61 @@
1
+ require 'active_support/concern'
2
+
3
+ module Kiosk
4
+ # Adds the ability to declare content rewrites in a controller.
5
+ #
6
+ module Controller
7
+ extend ActiveSupport::Concern
8
+
9
+ module InstanceMethods
10
+ # Declares a rewrite of content nodes that sets the host portion of
11
+ # each node's URI attribute (href or src) to target the configured CDN
12
+ # host.
13
+ #
14
+ def rewrite_cdn_paths_for(resource_model)
15
+ Kiosk.rewriter.add_rewrite(Rewrite.new(:cdn, resource_model))
16
+ end
17
+
18
+ # Declares a rewrite of content nodes.
19
+ #
20
+ # Example:
21
+ #
22
+ # class PostsController
23
+ # include Kiosk::Controller
24
+ #
25
+ # before_filter do
26
+ # rewrite_content_for(Attachment) do |attachment,node|
27
+ # case node.name
28
+ # when 'a'
29
+ # node['title'] = 'Some photo'
30
+ # when 'img'
31
+ # node['src'] = attachment_path(attachment.filename)
32
+ # end
33
+ # end
34
+ # end
35
+ # end
36
+ #
37
+ def rewrite_content_for(resource_model, &blk)
38
+ Kiosk.rewriter.add_rewrite(Rewrite.new(:node, resource_model, &blk))
39
+ end
40
+
41
+ # Declares a rewrite of content nodes that sets a new URL any href or src
42
+ # attributes (the first one found, it that order). The given block is
43
+ # passed the instantiated resource and node as its arguments and should
44
+ # return the new value for the node attribute.
45
+ #
46
+ # Example:
47
+ #
48
+ # class PostsController
49
+ # include Kiosk::Controller
50
+ #
51
+ # before_filter do
52
+ # rewrite_paths_for(Post) { |post| post_path(post.slug) }
53
+ # end
54
+ # end
55
+ #
56
+ def rewrite_paths_for(resource_model, &blk)
57
+ Kiosk.rewriter.add_rewrite(Rewrite.new(:path, resource_model, &blk))
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,6 @@
1
+ require 'nokogiri'
2
+
3
+ module Kiosk
4
+ class Document < Nokogiri::HTML::DocumentFragment
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module Kiosk
2
+ module Indexer
3
+ autoload :Adapter, 'kiosk/indexer/adapter'
4
+ end
5
+ end
@@ -0,0 +1,14 @@
1
+ module Kiosk
2
+ module Indexer
3
+ module Adapter
4
+ autoload :Base, 'kiosk/indexer/adapter/base'
5
+ autoload :ThinkingSphinxAdapter, 'kiosk/indexer/adapter/thinking_sphinx_adapter'
6
+
7
+ class << self
8
+ def new(type = :thinking_sphinx, *args)
9
+ "kiosk/indexer/adapter/#{type}_adapter".classify.constantize.new(*args)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,10 @@
1
+ module Kiosk::Indexer::Adapter
2
+ class Base
3
+ def extend_model(model)
4
+ model.class_exec(self.class.const_get(:Resource)) { |mixin| include mixin }
5
+ end
6
+
7
+ def index(name)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,266 @@
1
+ require 'active_support/concern'
2
+
3
+ module Kiosk::Indexer::Adapter
4
+ class ThinkingSphinxAdapter < Base
5
+
6
+ def index(name, io = STDOUT)
7
+ index = nil
8
+
9
+ ThinkingSphinx.context.indexed_resources.each do |res|
10
+ if i = res.constantize.sphinx_indexes.detect { |index| index.name == name }
11
+ index = i
12
+ end
13
+ end
14
+
15
+ index.write_xml_to(io) if index
16
+ end
17
+
18
+ module Resource
19
+ extend ActiveSupport::Concern
20
+
21
+ included do
22
+ # Mixin implementation for Thinking Sphinx
23
+ unless ThinkingSphinx.context.is_a?(Context)
24
+ ThinkingSphinx.context.extend(Context)
25
+ end
26
+
27
+ unless ThinkingSphinx::Configuration.instance.configuration.is_a?(Configuration)
28
+ ThinkingSphinx::Configuration.instance.configuration.extend(Configuration)
29
+ end
30
+ end
31
+
32
+ module ClassMethods
33
+ attr_accessor :sphinx_indexes
34
+
35
+ def define_index(name, &blk)
36
+ self.sphinx_indexes ||= []
37
+
38
+ ThinkingSphinx.context.add_indexed_resource self
39
+
40
+ index = Index.new(self, name.to_s)
41
+ index.instance_exec(&blk)
42
+
43
+ sphinx_indexes << index unless sphinx_indexes.any? { |i| i.name == index.name }
44
+ end
45
+
46
+ def search(*args)
47
+ options = args.extract_options!
48
+ query = args.first
49
+
50
+ Search.new(query, options).execute_for(self)
51
+ end
52
+
53
+ def sphinx_index_names
54
+ sphinx_indexes.collect { |index| index.name }
55
+ end
56
+
57
+ def to_riddle
58
+ if sphinx_indexes
59
+ sphinx_indexes.collect { |index| index.to_riddle }.flatten
60
+ else
61
+ []
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ class Search
68
+ attr_reader :query, :options
69
+
70
+ def initialize(query, options = {})
71
+ @query = query
72
+ @options = {:comment => ''}.merge(options)
73
+ @proxy = SearchProxy.new(@options)
74
+ end
75
+
76
+ def execute_for(model)
77
+ query = @proxy.star_query(@query)
78
+ using_options(@options) do
79
+ SearchResults.new(client.query(query, index_names_for(model), @options[:comment]), model, @options)
80
+ end
81
+ end
82
+
83
+ private
84
+
85
+ def using_options(options)
86
+ original_options = {}
87
+
88
+ options.each do |name,value|
89
+ if client.respond_to?("#{name}=")
90
+ original_options[name] = value
91
+ client.send("#{name}=", value)
92
+ end
93
+ end
94
+
95
+ result = yield
96
+
97
+ original_options.each do |name,value|
98
+ client.send("#{name}=", value)
99
+ end
100
+
101
+ result
102
+ end
103
+
104
+ def index_names_for(model)
105
+ model.sphinx_index_names.join(',')
106
+ end
107
+
108
+ def client
109
+ ThinkingSphinx::Configuration.instance.client
110
+ end
111
+ end
112
+
113
+ # Used to steal methods from +ThinkingSphinx::Search+.
114
+ #
115
+ class SearchProxy < ThinkingSphinx::Search
116
+ def initialize(options = {})
117
+ @options = options
118
+ end
119
+
120
+ def star_query(query)
121
+ @options[:star] ? super(query) : query
122
+ end
123
+ end
124
+
125
+ class SearchResults < Array
126
+ attr_reader :results
127
+
128
+ def initialize(sphinx_results, model, search_options)
129
+ @model = model
130
+ @results = sphinx_results
131
+ @options = search_options
132
+
133
+ populate if @results && @results[:matches]
134
+ end
135
+
136
+ def current_page
137
+ @options[:page].blank? ? 1 : @options[:page].to_i
138
+ end
139
+
140
+ def per_page
141
+ (@options[:per_page] || 20).to_i
142
+ end
143
+
144
+ def total_entries
145
+ @results[:total_found] || 0
146
+ end
147
+
148
+ def total_pages
149
+ (@results[:total] / per_page.to_f).ceil
150
+ end
151
+
152
+ private
153
+
154
+ def populate
155
+ replace(@results[:matches].collect { |match|
156
+ begin
157
+ @model.find(match[:doc])
158
+ rescue Kiosk::ResourceNotFound => e
159
+ @model.new
160
+ end
161
+ })
162
+ end
163
+ end
164
+
165
+ module Context
166
+ attr_reader :indexed_resources
167
+
168
+ def add_indexed_resource(resource)
169
+ resource = resource.name if resource.is_a?(Class)
170
+
171
+ @indexed_resources ||= []
172
+ @indexed_resources << resource unless @indexed_resources.include?(resource)
173
+ end
174
+ end
175
+
176
+ module Configuration
177
+ # Reimplement +Riddle::Configuration#render+ to include our own indexes
178
+ # in the config before it's rendered.
179
+ #
180
+ def render
181
+ ThinkingSphinx.context.indexed_resources.each do |resource|
182
+ resource.constantize.to_riddle.each do |index|
183
+ indices << index unless indices.any? { |i| i.name == index.name }
184
+ end
185
+ end
186
+
187
+ super
188
+ end
189
+ end
190
+
191
+ class Index
192
+ attr_reader :mode, :name
193
+
194
+ def initialize(model, name)
195
+ @model = model
196
+ @name = name
197
+
198
+ @fields = []
199
+ end
200
+
201
+ def indexes(*fields)
202
+ @fields += fields.collect { |field| Field.new(field) }
203
+ end
204
+
205
+ def config
206
+ @config ||= ThinkingSphinx::Configuration.instance
207
+ end
208
+
209
+ def to_riddle
210
+ index = Riddle::Configuration::Index.new(@name)
211
+ index.path = File.join(config.searchd_file_path, index.name)
212
+
213
+ config.index_options.each do |key,value|
214
+ method = "#{key}=".to_sym
215
+ index.send(method, value) if index.respond_to?(method)
216
+ end
217
+
218
+ source = Riddle::Configuration::XMLSource.new("#{@name}_source", :xmlpipe2)
219
+ source.xmlpipe_command = rake("kiosk:index[#{name}]")
220
+
221
+ index.sources << source
222
+
223
+ [index]
224
+ end
225
+
226
+ def write_xml_to(io)
227
+ xm = Builder::XmlMarkup.new(:target => io)
228
+
229
+ xm.instruct!
230
+ xm.sphinx :docset, 'xmlns:sphinx' => 'http://sphinxsearch.com' do
231
+ xm.sphinx :schema do
232
+ @fields.each { |field| field.to_xml(xm) }
233
+ end
234
+
235
+ @model.all.each do |resource|
236
+ xm.sphinx :document, :id => resource.id do
237
+ @fields.each do |field|
238
+ xm.send(field.name, resource.send(field.name))
239
+ end
240
+ end
241
+ end
242
+ end
243
+ end
244
+
245
+ private
246
+
247
+ def rake(task)
248
+ "#{Gem.bin_path('rake', 'rake')} --silent -f #{Rails.root}/Rakefile #{task}"
249
+ end
250
+ end
251
+
252
+ class Field
253
+ attr_reader :name
254
+
255
+ def initialize(name)
256
+ name = name.to_s
257
+ raise ArgumentError.new('invalid name') unless name =~ /^\w+$/
258
+ @name = name
259
+ end
260
+
261
+ def to_xml(builder)
262
+ builder.sphinx :field, :name => name
263
+ end
264
+ end
265
+ end
266
+ end
@@ -0,0 +1,5 @@
1
+ module Kiosk
2
+ module Localizable
3
+ autoload :Resource, 'kiosk/localizable/resource'
4
+ end
5
+ end
@@ -0,0 +1,30 @@
1
+ require 'active_support/concern'
2
+
3
+ module Kiosk
4
+ # Provides integration between I18n and WordPress for automatic retrieval of
5
+ # content translated to the language of the user session. This module
6
+ # depends on the installation of the WPML-JSON-API WordPress plugin.
7
+ #
8
+ module Localizable::Resource
9
+ extend ActiveSupport::Concern
10
+
11
+ module ClassMethods
12
+ attr_accessor :locale_scope_stack
13
+
14
+ # Executes the given block within a scope that limits found content
15
+ # resources to the given locale.
16
+ #
17
+ def with_locale(locale, &blk)
18
+ with_parameters(:language => locale, &blk)
19
+ end
20
+
21
+ # Executes the given block within a scope that translates found content
22
+ # resources to the given locale. See +Resource.with_params+ for details
23
+ # on scope execution and inheritance.
24
+ #
25
+ def localized_to(locale, &blk)
26
+ with_parameters(:to_language => locale, &blk)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,17 @@
1
+ require 'i18n'
2
+
3
+ module Kiosk
4
+ # Provides an +ActionController+ filter that scopes all content resource
5
+ # calls to only fetch content for the default locale and localize it all to
6
+ # the current request locale.
7
+ #
8
+ module Localizer
9
+ def self.around(controller)
10
+ Resource.with_locale(Kiosk.origin.default_locale || I18n.default_locale) do
11
+ Resource.localized_to(I18n.locale) do
12
+ yield
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,20 @@
1
+ module Kiosk
2
+ class Origin
3
+ attr_reader :site, :indexer, :default_locale, :cdn
4
+
5
+ def initialize(config)
6
+ @site = (config['site'] || '').sub(/\/+$/, '') + '/'
7
+ @indexer = Kiosk::Indexer::Adapter.new(config['indexer']) if config['indexer']
8
+ @default_locale = config['default_locale']
9
+ @cdn = Kiosk::Cdn.new(config['cdn'] || {})
10
+ end
11
+
12
+ def searcher
13
+ @indexer
14
+ end
15
+
16
+ def site_uri
17
+ URI.parse(site)
18
+ end
19
+ end
20
+ end