texter 0.1.0

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.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,12 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // the compiled file.
9
+ //
10
+ // WARNING: THE FIRST BLANK LINE MARKS THE END OF WHAT'S TO BE PROCESSED, ANY BLANK LINE SHOULD
11
+ // GO AFTER THE REQUIRES BELOW.
12
+ //
@@ -0,0 +1,33 @@
1
+ $(function(){
2
+ $('.js-edit').live('click', function(){
3
+ EditText($(this));
4
+ });
5
+
6
+ function EditText(elem){
7
+
8
+ function removePopup(){
9
+ popup.fadeOut('fast', function(){
10
+ popup.remove();
11
+ });
12
+ $(window).off('keyup');
13
+ }
14
+
15
+ $('body').append('<div id="popup"><span class="close"></span><div id="form"></div></div>');
16
+ var popup = $('#popup');
17
+ $(window).on('keyup', function(e){
18
+ if(e.keyCode == 27){
19
+ removePopup();
20
+ }
21
+ });
22
+
23
+ popup.find('.close').on('click', function(){
24
+ removePopup();
25
+ });
26
+
27
+ $.get(elem.data('url'), function(){
28
+ popup.fadeIn('fast', function(){
29
+ popup.find('textarea').filter(':first').focus();
30
+ });
31
+ });
32
+ }
33
+ });
@@ -0,0 +1,63 @@
1
+ #popup {
2
+ z-index:100;
3
+ display:none;
4
+ position:fixed;
5
+ width:500px;
6
+ left:50%;
7
+ margin-left:-250px;
8
+ top:150px;
9
+ background:#fff;
10
+ border:5px solid #ccc;
11
+ -webkit-border-radius: 10px;
12
+ -moz-border-radius: 10px;
13
+ -ms-border-radius: 10px;
14
+ -o-border-radius: 10px;
15
+ border-radius: 10px;
16
+
17
+ .close {
18
+ background: image-url('texter/close-icon.gif') no-repeat;
19
+ z-index:2;
20
+ display:block;
21
+ position:absolute;
22
+ right:10px;
23
+ top:10px;
24
+ width:16px;
25
+ height:16px;
26
+ cursor:pointer;
27
+ }
28
+
29
+ label, .title {
30
+ display:block;
31
+ margin-bottom:6px;
32
+ color: #000000;
33
+ abbr { color: red; }
34
+ }
35
+
36
+ .b-form-buttons {
37
+ position:relative;
38
+ margin-top:15px;
39
+ }
40
+
41
+ .input {
42
+ margin-bottom:20px;
43
+ }
44
+
45
+ #form {
46
+ padding:40px;
47
+
48
+ textarea {
49
+ width:410px;
50
+ height:180px;
51
+ border:1px solid #cacaca;
52
+ resize:none;
53
+ }
54
+ }
55
+ }
56
+
57
+ .js-edit {
58
+ cursor:pointer;
59
+
60
+ &:hover {
61
+ background:#fffb8f;
62
+ }
63
+ }
@@ -0,0 +1,12 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the top of the
9
+ * compiled file, but it's generally better to create a new file per style scope.
10
+ *
11
+ *= require_self
12
+ */
@@ -0,0 +1,2 @@
1
+ class Texter::ApplicationController < ApplicationController
2
+ end
@@ -0,0 +1,31 @@
1
+ require_dependency "texter/application_controller"
2
+
3
+ module Texter
4
+ class TextsController < ApplicationController
5
+ Texter.controller_setup.call(self)
6
+
7
+ respond_to :js
8
+ before_filter :load_text
9
+
10
+ def edit
11
+ end
12
+
13
+ def update
14
+ Texter::CleanBody.new(@text).clean(Texter.bodies & @text.changed)
15
+
16
+ if @text.valid? && Texter::Typograph.new(@text).process(Texter.bodies & @text.changed)
17
+ @text = TextDecorator.new(@text)
18
+ render :update
19
+ else
20
+ render :edit
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def load_text
27
+ @text = Text.find_or_create_from_translations_by_path(params[:id])
28
+ @text.attributes = params[:text]
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,39 @@
1
+ # encoding: UTF-8
2
+ module Texter
3
+ class TextDecorator < Draper::Base
4
+ decorates 'Texter::Text'
5
+
6
+ def body
7
+ return textile unless can_edit?
8
+
9
+ h.content_tag(content_tag, textile, {
10
+ :data => {
11
+ :url => h.texter.edit_text_path(path, :js, :text => {
12
+ :tag_type => tag_type
13
+ })
14
+ },
15
+ :class => "js-edit #{path_for_class}"
16
+ })
17
+ end
18
+
19
+ def path_for_class
20
+ path.gsub(/\./, '-')
21
+ end
22
+
23
+ private
24
+
25
+ def content_tag
26
+ {:block => :div, :inline => :span}[tag_type.to_sym]
27
+ end
28
+
29
+ def textile
30
+ body = get_body
31
+ body = "Редактировать" if can_edit? && body.blank?
32
+ h.send("textile_#{tag_type}", body)
33
+ end
34
+
35
+ def can_edit?
36
+ h.text_can_be_edited?(model)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,4 @@
1
+ module Texter
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,44 @@
1
+ module Texter
2
+ module TextsHelper
3
+ def inline(path)
4
+ display_text(path, :inline)
5
+ end
6
+
7
+ def block(path)
8
+ display_text(path, :block)
9
+ end
10
+
11
+ def display_text(path, tag_type)
12
+ text = Texter::TextFactory.new(path, tag_type, @virtual_path).build
13
+ text.body
14
+ end
15
+
16
+ def text_can_be_edited?(text)
17
+ moderator_signed_in?
18
+ end
19
+
20
+ def textile(string, *rules)
21
+ return nil if string.blank?
22
+
23
+ textilize = ::RedCloth.new(string, rules)
24
+ textilize.to_html(:block_textile_prefix, :block_textile_lists, :inline_textile_link).html_safe
25
+
26
+ # refs_textile:: Textile references (i.e. [hobix]http://hobix.com/)
27
+ # block_textile_table:: Textile table block structures
28
+ # block_textile_lists:: Textile list structures
29
+ # block_textile_prefix:: Textile blocks with prefixes (i.e. bq., h2., etc.)
30
+ # inline_textile_image:: Textile inline images
31
+ # inline_textile_link:: Textile inline links
32
+ # inline_textile_span:: Textile inline spans
33
+ # inline_textile_glyphs:: Textile entities (such as em-dashes and smart quotes)
34
+ end
35
+
36
+ def textile_block(string)
37
+ textile(string)
38
+ end
39
+
40
+ def textile_inline(string)
41
+ textile(string, :lite_mode)
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,33 @@
1
+ module Texter
2
+ class Text < ActiveRecord::Base
3
+ TAG_TYPES = %w{block inline}
4
+ attr_writer :tag_type
5
+
6
+ attr_accessible *Texter.bodies, :tag_type
7
+
8
+ validates_uniqueness_of :path, :allow_blank => false
9
+ validates_presence_of *Texter.bodies
10
+
11
+ def self.find_or_create_from_translations_by_path(path)
12
+ text = find_or_initialize_by_path(path)
13
+ text.new_record? && text.update_attributes(text.default_attributes)
14
+ text
15
+ end
16
+
17
+ def to_param
18
+ path
19
+ end
20
+
21
+ def get_body(options = {})
22
+ persisted? ? body : I18n.t(path, {:scope => 'texts'}.merge(options)).strip
23
+ end
24
+
25
+ def tag_type
26
+ TAG_TYPES.include?(@tag_type.to_s) ? @tag_type : TAG_TYPES.first
27
+ end
28
+
29
+ def default_attributes
30
+ {:body => get_body}
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,7 @@
1
+ <%= simple_form_for @text, :remote => true do |f| %>
2
+ <%= f.input :tag_type, :as => :hidden %>
3
+ <% Texter.bodies.each do |body| %>
4
+ <%= f.input body %>
5
+ <% end %>
6
+ <%= f.submit "Сохранить", :disable_with => '✔ Сохраняется...' %>
7
+ <% end %>
@@ -0,0 +1 @@
1
+ $("#form").html($("<%= j(render("edit")) %>"));
@@ -0,0 +1,4 @@
1
+ $("#popup").remove();
2
+ $(".<%= @text.path_for_class %>").each(function(){
3
+ $(this).replaceWith($("<%= j(@text.body) %>"));
4
+ });
data/config/routes.rb ADDED
@@ -0,0 +1,3 @@
1
+ Texter::Engine.routes.draw do
2
+ resources :texts, :only => [:edit, :update], :id => /[\w\.]+/
3
+ end
@@ -0,0 +1,10 @@
1
+ class CreateTexterTexts < ActiveRecord::Migration
2
+ def change
3
+ create_table :texter_texts do |t|
4
+ t.string :path, :null => false, :default => ''
5
+ t.text :body
6
+ t.timestamps
7
+ end
8
+ add_index :texter_texts, :path, :unique => true
9
+ end
10
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :texter do
3
+ # # Task goes here
4
+ # end
data/lib/texter.rb ADDED
@@ -0,0 +1,11 @@
1
+ require "texter/engine"
2
+
3
+ module Texter
4
+ # body attributes for text, array of strings
5
+ # it can be %w{body_ru body_en} for instance, if you use multiple languages
6
+ mattr_accessor :bodies
7
+ self.bodies = %w{body}
8
+
9
+ mattr_accessor :controller_setup
10
+ self.controller_setup = proc{|controller| controller.before_filter(:require_moderator!)}
11
+ end
@@ -0,0 +1,11 @@
1
+ module Texter
2
+ class CleanBody < Struct.new(:text)
3
+ def clean(*attrs)
4
+ attrs.flatten.each do |attr|
5
+ if (value = text.send(attr)).present?
6
+ text.send("#{attr}=", value.gsub(/\r/, ''))
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,19 @@
1
+ require 'draper'
2
+ require 'simple_form'
3
+ require 'RedCloth'
4
+ require 'art_typograph'
5
+ require 'texter/typograph'
6
+ require 'texter/clean_body'
7
+ require 'texter/text_factory'
8
+
9
+ module Texter
10
+ class Engine < ::Rails::Engine
11
+ isolate_namespace Texter
12
+
13
+ initializer 'texter.action_controller' do |app|
14
+ ActiveSupport.on_load :action_controller do
15
+ helper Texter::TextsHelper
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,23 @@
1
+ module Texter
2
+ class TextFactory < Struct.new(:path, :tag_type, :virtual_path)
3
+ def build
4
+ text = Texter::Text.find_or_initialize_by_path(full_path)
5
+ text.tag_type = tag_type
6
+ Texter::TextDecorator.new(text)
7
+ end
8
+
9
+ private
10
+
11
+ def full_path
12
+ if path.to_s.first == "."
13
+ if virtual_path
14
+ virtual_path.gsub(%r{/_?}, ".") + path.to_s
15
+ else
16
+ raise "Cannot use #{path.inspect} shortcut because path is not available"
17
+ end
18
+ else
19
+ path
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,21 @@
1
+ module Texter
2
+ class Typograph < Struct.new(:text)
3
+ def process(*attrs)
4
+ return true if attrs.blank?
5
+
6
+ options = attrs.flatten.inject({}){|memo, attr|
7
+ memo.merge! attr => self.class.process(text.send(attr))
8
+ }
9
+
10
+ text.update_attributes options
11
+ end
12
+
13
+ def self.process(text)
14
+ s = ArtTypograph.process(text)
15
+ s.gsub! %r{</p>}m, "\n"
16
+ s.gsub! %r{<p.*?>}, ''
17
+ s.gsub! %r{</?(nobr|span).*?>}, ''
18
+ s.strip
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,3 @@
1
+ module Texter
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,237 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: texter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Max Savchenko
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-01-03 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rails
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 3.2.9
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 3.2.9
30
+ - !ruby/object:Gem::Dependency
31
+ name: sass-rails
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 3.2.3
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 3.2.3
46
+ - !ruby/object:Gem::Dependency
47
+ name: draper
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: RedCloth
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 4.2.9
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: 4.2.9
78
+ - !ruby/object:Gem::Dependency
79
+ name: simple_form
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: art_typograph
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :runtime
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: rspec-rails
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ~>
116
+ - !ruby/object:Gem::Version
117
+ version: '2.12'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ~>
124
+ - !ruby/object:Gem::Version
125
+ version: '2.12'
126
+ - !ruby/object:Gem::Dependency
127
+ name: capybara
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ~>
132
+ - !ruby/object:Gem::Version
133
+ version: 2.0.1
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ~>
140
+ - !ruby/object:Gem::Version
141
+ version: 2.0.1
142
+ - !ruby/object:Gem::Dependency
143
+ name: sqlite3
144
+ requirement: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ! '>='
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ type: :development
151
+ prerelease: false
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ - !ruby/object:Gem::Dependency
159
+ name: database_cleaner
160
+ requirement: !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - ! '>='
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ none: false
170
+ requirements:
171
+ - - ! '>='
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ description: ! '* Лёгкий вывод блочных и инлайновых текстов, отформатированных с помощью
175
+ Textile
176
+
177
+ * Дефолтные тексты в I18n
178
+
179
+ * После редактирования тексты хранятся в базе, перед сохранением обрабатываются
180
+ Типографом Лебедева
181
+
182
+ * Удобный, встроенный в сайт интерфейс редактирования'
183
+ email:
184
+ - robotector@gmail.com
185
+ executables: []
186
+ extensions: []
187
+ extra_rdoc_files: []
188
+ files:
189
+ - app/assets/images/texter/close-icon.gif
190
+ - app/assets/javascripts/texter/application.js
191
+ - app/assets/javascripts/texter/texts.js
192
+ - app/assets/stylesheets/texter/_texts.scss
193
+ - app/assets/stylesheets/texter/application.css
194
+ - app/controllers/texter/application_controller.rb
195
+ - app/controllers/texter/texts_controller.rb
196
+ - app/decorators/texter/text_decorator.rb
197
+ - app/helpers/texter/application_helper.rb
198
+ - app/helpers/texter/texts_helper.rb
199
+ - app/models/texter/text.rb
200
+ - app/views/texter/texts/_edit.html.erb
201
+ - app/views/texter/texts/edit.js.erb
202
+ - app/views/texter/texts/update.js.erb
203
+ - config/routes.rb
204
+ - db/migrate/20121231021051_create_texter_texts.rb
205
+ - lib/tasks/texter_tasks.rake
206
+ - lib/texter/clean_body.rb
207
+ - lib/texter/engine.rb
208
+ - lib/texter/text_factory.rb
209
+ - lib/texter/typograph.rb
210
+ - lib/texter/version.rb
211
+ - lib/texter.rb
212
+ - Rakefile
213
+ homepage: http://github.com/macovsky/texter
214
+ licenses: []
215
+ post_install_message:
216
+ rdoc_options: []
217
+ require_paths:
218
+ - lib
219
+ required_ruby_version: !ruby/object:Gem::Requirement
220
+ none: false
221
+ requirements:
222
+ - - ! '>='
223
+ - !ruby/object:Gem::Version
224
+ version: '0'
225
+ required_rubygems_version: !ruby/object:Gem::Requirement
226
+ none: false
227
+ requirements:
228
+ - - ! '>='
229
+ - !ruby/object:Gem::Version
230
+ version: '0'
231
+ requirements: []
232
+ rubyforge_project:
233
+ rubygems_version: 1.8.23
234
+ signing_key:
235
+ specification_version: 3
236
+ summary: Rails-engine для редактирования блоков текста, с поддержкой I18n
237
+ test_files: []