tight-engine 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 065ac37dc49f5b5e67ec4271bb92f71eee632e0f
4
- data.tar.gz: 72dfcdf5eae3c379407c7c698293515e59822db8
3
+ metadata.gz: d9af08abd886546c991e706361e0a40872e0e6ca
4
+ data.tar.gz: b9e8bb833bf96cfbc027ff4f446c41b085930b1c
5
5
  SHA512:
6
- metadata.gz: 50b1e579488cad7dc14f878f8e2605e8d1837500de6e980aa175f14839693e7027d5df4dd078ef2d86a085e1714ee0f94988976cb39ba56b547dfad6a8b00ea9
7
- data.tar.gz: 0bc29fa1374faeb73b5949bc6f504f613fad53d39eac471350c69dfae7c6a78ae94e5429a6a927f3aba1eb4c6bb6255791c3112cd72c175f05f231448a13d3eb
6
+ metadata.gz: da17884b6953ce5babcb31c05a577f114c56f16ae41dde952840a849c0d6f70ea297d92c33025da579b3a491ded507a523d1d810a827cb66b3db962da1280aab
7
+ data.tar.gz: 1e6484ce7ec08c592a25c9fb9575f853b8826986ca8868883ad3eab2a94d4a95d639bed33d8f7f024ef47573aa8c60b3ff649261ad85cf2120f790457c49d618
data/.gitignore CHANGED
@@ -1,2 +1,3 @@
1
1
  *.rbc
2
2
  *.sassc
3
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1 @@
1
+ gemspec
@@ -0,0 +1,23 @@
1
+ require 'tight-engine/init'
2
+ require 'tight-engine/locale'
3
+ require 'tight-engine/defer'
4
+ require 'tight-engine/template'
5
+ require 'tight-engine/render'
6
+ require 'tight-engine/url'
7
+ require 'tight-engine/utils'
8
+
9
+ module Tight
10
+ module Engine
11
+ def self.registered(app)
12
+ app.send(:include, Tight::Engine::Init)
13
+ app.send(:include, Tight::Engine::Locale)
14
+ app.send(:include, Tight::Engine::Defer)
15
+ app.send(:include, Tight::Engine::Template)
16
+ app.send(:include, Tight::Engine::Render)
17
+ app.send(:include, Tight::Engine::Url)
18
+ app.send(:include, Tight::Engine::Utils)
19
+
20
+ app.default :deferred_elements, []
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,30 @@
1
+ module Tight
2
+ module Engine
3
+ module Defer
4
+ def placeholders
5
+ @placeholders ||= {}
6
+ end
7
+
8
+ def defer_element( name, args, opts )
9
+ return unless settings.deferred_elements.include?(name)
10
+ placeholders[name] = [ name, args, opts ]
11
+ "%{placeholder[:#{name}]}"
12
+ end
13
+
14
+ def process_deferred_elements( text )
15
+ text.to_str.gsub /(\s*)\%\{placeholder\[\:([^\]]+)\]\}/ do
16
+ if deferred = placeholders[$2]
17
+ output = process_element *deferred
18
+ if engine.pretty?
19
+ $1+output.gsub(/\r|\n|\r\n/, $1)+$1
20
+ else
21
+ output
22
+ end
23
+ else
24
+ ''
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,120 @@
1
+ require 'ostruct'
2
+
3
+ module Tight
4
+ module Engine
5
+ module Init
6
+ def engine
7
+ @tight_env ||= OpenStruct.new
8
+ end
9
+
10
+ def init_page
11
+ path = normalize_path swift.path
12
+ page = find_page_or_module path
13
+ redirect_if_blank page
14
+ merge_default_params page
15
+ init_path_pages page
16
+ @page = page
17
+ end
18
+
19
+ def init_error( errno )
20
+ init_path_pages
21
+ @page = Page.first :path => "/error/#{errno}"
22
+ @page ||= Page.new :title => "Error #{errno}", :text => "page /error/#{errno} not found"
23
+ end
24
+
25
+ def init_swift
26
+ return if @_inited
27
+ init_folders
28
+ init_media
29
+ init_locale
30
+ init_http
31
+ @_inited = true
32
+ end
33
+
34
+ private
35
+
36
+ def find_page_or_module( path )
37
+ page = Page.first( :conditions => [ "? LIKE IF(is_module,CONCAT(path,'%'),path)", path ], :order => :path.desc )
38
+ detect_module_slug page, path
39
+ end
40
+
41
+ def normalize_path( path )
42
+ path.squeeze! '/'
43
+ path[1] ? path.chomp('/') : path
44
+ end
45
+
46
+ def redirect_if_blank( page )
47
+ if page && page.parent_id && page.fragment_id == 'page' && page.text.blank? && !page.is_module
48
+ first_child = page.children.first( :order => :position )
49
+ redirect first_child.path if first_child
50
+ end
51
+ end
52
+
53
+ def merge_default_params( page )
54
+ params.reverse_merge! Rack::Utils.parse_query(page.params) if page && page.params.present?
55
+ end
56
+
57
+ def detect_module_slug( page, path )
58
+ if page && page.is_module
59
+ swift.module_root = page.path
60
+ swift.slug = case path[page.path.length]
61
+ when '/'
62
+ path[(page.path.length+1)..-1]
63
+ when nil
64
+ ''
65
+ else
66
+ page = nil
67
+ end
68
+ end
69
+ page
70
+ end
71
+
72
+ def init_path_pages( page = Page.root )
73
+ while page
74
+ swift.path_pages.unshift page
75
+ swift.path_ids.unshift page.id
76
+ page = page.parent
77
+ end
78
+ end
79
+
80
+ def init_http
81
+ swift.http_method = request.env['REQUEST_METHOD']
82
+ swift.host = request.env['SERVER_NAME']
83
+ swift.request = request.env['REQUEST_URI']
84
+ swift.uri = "/#{params[:request_uri]}"
85
+ swift.path = swift.uri.partition('?').first
86
+ swift.path_pages = []
87
+ swift.path_ids = []
88
+ end
89
+
90
+ def init_folders
91
+ swift.root = Swift::Application.root
92
+ swift.public = Swift::Application.public_folder
93
+ swift.views = Swift::Application.views
94
+ end
95
+
96
+ def init_media
97
+ media = params.has_key?('print') ? 'print' : 'screen'
98
+ swift.send("#{media}?=", media)
99
+ swift.media = media
100
+ swift.send("pretty?=", Padrino.env == :development)
101
+ end
102
+
103
+ def init_locale
104
+ swift.locales = Option(:locales) || %w(ru en)
105
+ swift.locale = params[:locale] ? detect_selected_locale : detect_session_locale
106
+ session[:locale] = swift.locale
107
+ I18n.available_locales = swift.locales
108
+ I18n.locale = swift.locale
109
+ end
110
+
111
+ public
112
+
113
+ # deprecated
114
+ def swift
115
+ warn '#swift method is deprecated'
116
+ @tight_env ||= OpenStruct.new
117
+ end
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,40 @@
1
+ module Tight
2
+ module Engine
3
+ module Locale
4
+ private
5
+
6
+ def detect_selected_locale
7
+ selected_locale = params[:locale].to_s[0..1]
8
+ if swift.locales.include?(selected_locale)
9
+ selected_locale.to_sym
10
+ else
11
+ detect_session_locale
12
+ end
13
+ end
14
+
15
+ def detect_session_locale
16
+ if session[:locale].present? && swift.locales.include?(session[:locale].to_s)
17
+ session[:locale].to_sym
18
+ else
19
+ detect_preferred_locale
20
+ end
21
+ end
22
+
23
+ def detect_preferred_locale
24
+ detected_locale = if preferred_languages = request.env['HTTP_ACCEPT_LANGUAGE']
25
+ (parse_http_accept_language(preferred_languages) & swift.locales).first
26
+ end
27
+ detected_locale ||= swift.locales.first
28
+ detected_locale.to_sym
29
+ end
30
+
31
+ def parse_http_accept_language( languages )
32
+ languages.gsub(/\s+/,'').split(/,/)
33
+ .sort_by{ |tags| -(tags.partition(/;/).last.split(/=/)[1]||1).to_f }
34
+ .map{ |language| language[0..1] }.uniq
35
+ rescue # !!! FIXME detect valid Exceptions
36
+ []
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,193 @@
1
+ module Tight
2
+ module Engine
3
+ module Render
4
+ MAX_PARSE_LEVEL = 4
5
+
6
+ REGEX_RECURSIVE_BRACKETS = /
7
+ (?<re> #
8
+ \[ #
9
+ (?: #
10
+ (?>[^\[\]]+) # 1
11
+ | #
12
+ \g<re> #
13
+ )* #
14
+ \] #
15
+ ) #
16
+ /x.freeze
17
+
18
+ def strip_code( text )
19
+ text && text.gsub(REGEX_RECURSIVE_BRACKETS, '').strip
20
+ end
21
+
22
+ def engine_render( text )
23
+ Markdown.render( parse_content( text.to_s ) ).html_safe
24
+ end
25
+
26
+ def inline_render( text )
27
+ engine_render( text ).gsub(/^<p>(.*)<\/p>$/, '\1').chomp
28
+ end
29
+
30
+ private
31
+
32
+ def parse_content( text )
33
+ limit_recursion do |flags|
34
+ draft = text.gsub(REGEX_RECURSIVE_BRACKETS) do |tag|
35
+ internal_tag(tag) || external_tag(tag, flags)
36
+ end
37
+ capture_tag_content draft, flags
38
+ end
39
+ end
40
+
41
+ INTERNAL_TAGS = {
42
+ 'page' => 'PageLink',
43
+ 'link' => 'PageLink',
44
+ 'block' => 'Block',
45
+ 'table' => 'Block',
46
+ 'image' => 'Image',
47
+ 'img' => 'Image',
48
+ 'file' => 'File',
49
+ 'asset' => 'File',
50
+ 'element' => :self,
51
+ }.freeze
52
+
53
+ def dispatch_element( tag_name, args, opts )
54
+ element_name = INTERNAL_TAGS[tag_name] or return
55
+ element_name = args.shift if element_name == :self
56
+ process_element element_name, args, opts
57
+ end
58
+
59
+ REGEX_INTERNAL_TAG = /
60
+ \[ # [
61
+ (#{INTERNAL_TAGS.keys.join('|')}) # 1, -- tag name
62
+ ( # 2, -- identity
63
+ (?:[\:\.\#][\w\-]*)* #
64
+ ) #
65
+ \s+ #
66
+ (.*) # 3, -- arguments
67
+ \] # ]
68
+ /x.freeze
69
+
70
+ def internal_tag( tag )
71
+ data = tag.match(REGEX_INTERNAL_TAG) or return
72
+ tag_name, identity, vars = data[1..-1]
73
+ args, opts = parse_vars vars
74
+ opts[:title] = detect_title( tag_name, args ) if opts[:title].blank?
75
+ detect_identity identity, opts
76
+ dispatch_element tag_name, args, opts
77
+ end
78
+
79
+ def external_tag( tag, flags )
80
+ tag_name, _, vars = tag[1..-2].partition ' '
81
+ code = Code.first( :slug => tag_name ) unless tag_name[0] == '/'
82
+ if code && code.is_single
83
+ args, opts = parse_vars vars
84
+ parse_code( code.html, args )
85
+ else
86
+ flags[:needs_capturing] = true if code
87
+ tag
88
+ end
89
+ end
90
+
91
+ REGEX_IDENTITY = /
92
+ [\:\.\#] # prefix :.#
93
+ [\w\-]* # identity name
94
+ /x.freeze
95
+
96
+ def detect_identity( identity, opts )
97
+ identity.to_s.scan(REGEX_IDENTITY).each do |attr|
98
+ prefix, name = attr[0], attr[1..-1]
99
+ case prefix
100
+ when '#'
101
+ opts[:id] ||= name
102
+ when '.'
103
+ if opts[:class].blank?
104
+ opts[:class] = name
105
+ else
106
+ opts[:class] << ' ' << name
107
+ end
108
+ when ':'
109
+ opts[:instance] = name
110
+ end
111
+ end
112
+ end
113
+
114
+ def detect_title( type, args )
115
+ newtitle = if type == 'element'
116
+ args[2..-1]||[]
117
+ else
118
+ args[1..-1]||[]
119
+ end.join(' ').strip
120
+ parse_content(newtitle) if newtitle.present?
121
+ end
122
+
123
+ def capture_tag_content( str, flags )
124
+ return str unless flags[:needs_capturing]
125
+ str.gsub( /\[([^\s]*)\s*(.*?)\](.*?)\[\/(\1)\]/m ) do |s|
126
+ args, _ = parse_vars $2
127
+ code = Code.by_slug $1 #!!! FIXME database call
128
+ parse_code code.html, args, $3
129
+ end
130
+ end
131
+
132
+ def limit_recursion
133
+ @parse_level ||= 0
134
+ @parse_level += 1
135
+ raise SystemStackError, 'parse level too deep' if @parse_level > MAX_PARSE_LEVEL
136
+ result = yield({})
137
+ @parse_level -= 1
138
+ result
139
+ end
140
+
141
+ REGEX_VARS = /
142
+ ( # 0
143
+ ([\S^,]+)\:\s* # 1
144
+ (?: #
145
+ ["']([^"']+)["'] | # 2
146
+ ([^,'"\s]+) # 3 "'
147
+ ) #
148
+ ),? | #
149
+ ( # 4
150
+ ([^'"\s]+) | # 5 "'
151
+ ["']([^"']+)["'],? # 6
152
+ )
153
+ /x.freeze
154
+
155
+ def parse_vars( vars )
156
+ args = []
157
+ opts = {}
158
+ vars.scan(REGEX_VARS).each do |v|
159
+ opts[v[1].to_sym] = ( v[2] || v[3] ) if v[0]
160
+ args << ( v[5] || v[6] ) if v[4]
161
+ end
162
+ [args, opts]
163
+ end
164
+
165
+ REGEX_EXTERNAL_TAG = /
166
+ \[ #
167
+ (\d+) # 1
168
+ (?: #
169
+ \: #
170
+ (.*?) # 2
171
+ )? #
172
+ \] #
173
+ | #
174
+ \[ #
175
+ (content) # 3
176
+ \] #
177
+ /x.freeze
178
+
179
+ def parse_code( html, args, content = '' )
180
+ html.gsub(REGEX_EXTERNAL_TAG) do |s|
181
+ idx = $1.to_i
182
+ if idx > 0
183
+ (args[idx-1] || $2).to_s
184
+ elsif $3 == 'content'
185
+ parse_content content
186
+ else
187
+ "[#{tag}]"
188
+ end
189
+ end
190
+ end
191
+ end
192
+ end
193
+ end
@@ -0,0 +1,60 @@
1
+ module Tight
2
+ module Engine
3
+ module Template
4
+ def process_page
5
+ process_deferred_elements fragment( @page.fragment_id, :layout => :"layouts/#{@page.layout_id}" )
6
+ end
7
+
8
+ def element( name, *args )
9
+ opts = Hash === args.last ? args.pop : {}
10
+ defer_element( name, args, opts ) || process_element( name, args, opts )
11
+ end
12
+
13
+ def element_view( name, opts = {} )
14
+ fragment name, :elements, opts
15
+ end
16
+
17
+ def fragment( template, type = nil, opts = {} )
18
+ opts, type = type, nil if type.kind_of? Hash
19
+ opts[:layout] ||= false
20
+ type ||= :fragments
21
+ render :slim, :"#{type}/#{template}", opts
22
+ rescue Padrino::Rendering::TemplateNotFound, Errno::ENOENT => e
23
+ name = template.partition('/').first
24
+ report_error e, "EngineHelpers##{__method__}@#{__LINE__}", "[#{type.to_s.singularize.camelize} '#{name}' error: #{e.to_s.strip}]"
25
+ end
26
+
27
+ private
28
+
29
+ def process_element( name, args, opts )
30
+ core, view = find_element name, opts[:instance]
31
+ fill_identity name, opts
32
+ catch :output do
33
+ @args, @opts = args, opts
34
+ binding.eval File.read(core), core if File.exists?(core)
35
+ render :slim, view.to_sym, :layout => false, :views => Swift::Application.views
36
+ end
37
+ rescue Padrino::Rendering::TemplateNotFound => e
38
+ report_error e, "EngineHelpers##{__method__}@#{__LINE__}", "[Element '#{name}' error: #{e.strip}]"
39
+ rescue Exception => e
40
+ report_error e, "EngineHelpers##{__method__}@#{__LINE__} '#{name}'"
41
+ end
42
+
43
+ def find_element( name, instance = nil )
44
+ view = "elements/#{name}/view"
45
+ if instance
46
+ instance_view = "#{view}-#{instance}"
47
+ view = instance_view if File.file?( "#{Swift::Application.views}/#{instance_view}.slim" )
48
+ end
49
+ [ "#{Swift::Application.views}/elements/#{name}/core.rb", view ]
50
+ end
51
+
52
+ def fill_identity( element_name, opts = {} )
53
+ @identity = { :class => element_name.dup }
54
+ @identity[:id] = opts[:id] if opts[:id]
55
+ @identity[:class] << " #{opts[:class]}" if opts[:class]
56
+ @identity[:class] << " #{element_name}-#{opts[:instance]}" if opts[:instance]
57
+ end
58
+ end
59
+ end
60
+ end