sofav 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +35 -0
- data/Rakefile +34 -0
- data/app/builders/admin/form_builder.rb +250 -0
- data/app/controllers/application_controller.rb +23 -0
- data/app/decorators/base_decorator.rb +172 -0
- data/app/helpers/admin/core_helper.rb +237 -0
- data/app/helpers/admin/show_view_helper.rb +69 -0
- data/app/helpers/admin/tags_helper.rb +104 -0
- data/app/services/active_record/search_service.rb +121 -0
- data/app/views/admin/application/_breadcrumb_nav.html.erb +10 -0
- data/app/views/admin/application/_collection.html.erb +79 -0
- data/app/views/admin/application/_save_js.js.erb +20 -0
- data/app/views/admin/application/_sidebar.html.erb +62 -0
- data/app/views/admin/application/_topbar.html.erb +32 -0
- data/app/views/admin/application/actions/charge.html.erb +3 -0
- data/app/views/admin/application/actions/destroy.html.erb +5 -0
- data/app/views/admin/application/actions/edit.html.erb +1 -0
- data/app/views/admin/application/actions/edit_sights.html.erb +1 -0
- data/app/views/admin/application/actions/feedback.html.erb +4 -0
- data/app/views/admin/application/actions/index_charge.html.erb +1 -0
- data/app/views/admin/application/actions/index_destroy.html.erb +1 -0
- data/app/views/admin/application/actions/index_edit.html.erb +1 -0
- data/app/views/admin/application/actions/index_edit_sights.html.erb +1 -0
- data/app/views/admin/application/actions/index_show.html.erb +1 -0
- data/app/views/admin/application/actions/new.html.erb +1 -0
- data/app/views/admin/application/create.js.erb +1 -0
- data/app/views/admin/application/destroy.js.erb +6 -0
- data/app/views/admin/application/edit.html.erb +18 -0
- data/app/views/admin/application/index.html.erb +23 -0
- data/app/views/admin/application/new.html.erb +18 -0
- data/app/views/admin/application/partials/_map_pos_picker.html.erb +101 -0
- data/app/views/admin/application/partials/_sort_script.html.erb +32 -0
- data/app/views/admin/application/show.html.erb +16 -0
- data/app/views/admin/application/update.js.erb +1 -0
- data/app/views/admin/dashboard/index.html.erb +8 -0
- data/app/views/layouts/admin.html.erb +30 -0
- data/config/locales/devise.en.yml +64 -0
- data/config/locales/devise.zh-CN.yml +120 -0
- data/config/locales/doorkeeper.en.yml +124 -0
- data/config/locales/doorkeeper.zh-CN.yml +132 -0
- data/config/locales/en.yml +23 -0
- data/config/locales/enumerize/defaults.zh-CN.yml +7 -0
- data/config/locales/kaminari.zh-CN.yml +17 -0
- data/config/locales/rails.zh-CN.yml +204 -0
- data/config/locales/views/actions.zh-CN.yml +21 -0
- data/config/locales/views/attributes.zh-CN.yml +15 -0
- data/config/locales/views/breadcrumb.zh-CN.yml +5 -0
- data/config/locales/views/common.zh-CN.yml +7 -0
- data/config/locales/views/enums.zh-CN.yml +13 -0
- data/config/locales/views/profiles.zh-CN.yml +3 -0
- data/lib/generators/sofav/USAGE +0 -0
- data/lib/generators/sofav/sofav_generator.rb +50 -0
- data/lib/generators/sofav/templates/activerecord.zh-CN.yml +4 -0
- data/lib/generators/sofav/templates/attribute_types.zh-CN.yml +2 -0
- data/lib/sofav.rb +6 -0
- data/lib/sofav/decorator.rb +47 -0
- data/lib/sofav/local.rb +52 -0
- data/lib/sofav/version.rb +3 -0
- data/lib/tasks/sofa_tasks.rake +4 -0
- data/vendor/assets/javascripts/admin/here.js +0 -0
- data/vendor/assets/javascripts/bootbox.js +1020 -0
- data/vendor/assets/javascripts/bootstrap.js +2377 -0
- data/vendor/assets/javascripts/jquery-ui.js +5169 -0
- data/vendor/assets/javascripts/select2.js +5725 -0
- data/vendor/assets/stylesheets/bootstrap.css +6800 -0
- data/vendor/assets/stylesheets/bootstrap.css.map +1 -0
- data/vendor/assets/stylesheets/jquery-ui.css +453 -0
- data/vendor/assets/stylesheets/select2.css +484 -0
- metadata +211 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: e60d0d8ba1013b0ea2c8d3cfa78c2d6243568a89
|
4
|
+
data.tar.gz: f7fd74fee86b39adbbda912402fdba14bacc4f6a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2bebb18fb8e41a8ed3dc0a41e8a37b0c18791f0339366d3652678ab8ff303b45ae2cc27072c257bd90295d2c9e5d4895858032591ccf09cd49daa03e99fff12f
|
7
|
+
data.tar.gz: 8ce5c387a0096a5b9a97d4c333644d6e30ab542a6de1ac77c051c4cce34cd4b6d7be963e61a4982a6db2902e067808330df48bd3a4929a30576e4b7ba8b6e7b2
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2017 yangdiei
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# Sofa
|
2
|
+
Short description and motivation.
|
3
|
+
|
4
|
+
## Usage
|
5
|
+
rails generate sofav NAME [field:type field:type] [options]
|
6
|
+
```
|
7
|
+
rails g sofav User name:string age:integer
|
8
|
+
```
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
Add this line to your application's Gemfile:
|
12
|
+
|
13
|
+
```ruby
|
14
|
+
gem 'sofav'
|
15
|
+
```
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
```bash
|
19
|
+
$ bundle
|
20
|
+
```
|
21
|
+
|
22
|
+
Or install it yourself as:
|
23
|
+
```bash
|
24
|
+
$ gem install sofa
|
25
|
+
```
|
26
|
+
|
27
|
+
## Development
|
28
|
+
|
29
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
|
30
|
+
|
31
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release` to create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
32
|
+
|
33
|
+
|
34
|
+
## License
|
35
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'Sofav'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.md')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
require 'bundler/gem_tasks'
|
23
|
+
|
24
|
+
require 'rake/testtask'
|
25
|
+
|
26
|
+
Rake::TestTask.new(:test) do |t|
|
27
|
+
t.libs << 'lib'
|
28
|
+
t.libs << 'test'
|
29
|
+
t.pattern = 'test/**/*_test.rb'
|
30
|
+
t.verbose = false
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
task default: :test
|
@@ -0,0 +1,250 @@
|
|
1
|
+
module Admin
|
2
|
+
class FormBuilder < ActionView::Helpers::FormBuilder
|
3
|
+
delegate :model, :decorator, :decorate, :record, :records, :concat, to: :@template
|
4
|
+
|
5
|
+
def label_tag(method, options = {})
|
6
|
+
label_class = Array(options[:label_class]) || []
|
7
|
+
label_class << ['control-label', options[:block] ? 'col-sm-2' : 'col-sm-4'] if label_class.blank?
|
8
|
+
label_class << 'required' if options[:required]
|
9
|
+
@template.content_tag(:label, model.human_attribute_name(method), class: label_class.flatten)
|
10
|
+
end
|
11
|
+
|
12
|
+
def input_dom_id(field_name)
|
13
|
+
'_form_id_'.concat(field_name.to_s)
|
14
|
+
end
|
15
|
+
|
16
|
+
def field_wrapper(method, options = {}, &block)
|
17
|
+
label_options = options.extract!(:label_class, :required, :block)
|
18
|
+
is_block = label_options[:block]
|
19
|
+
show_label = options.delete(:show_label)
|
20
|
+
options[:class] ||= 'form-control'
|
21
|
+
options.merge!(id: input_dom_id(method))
|
22
|
+
|
23
|
+
@template.content_tag(:li, class: is_block ? 'col-sm-12' : 'col-sm-6') do
|
24
|
+
@template.content_tag(:div, class: 'form-group') do
|
25
|
+
content = (show_label.nil? || show_label) ? [label_tag(method, label_options)] : [@template.content_tag(:div, class: 'col-sm-2'){}]
|
26
|
+
content << @template.content_tag(:div, yield(block), class: is_block ? 'col-sm-10' : 'col-sm-8')
|
27
|
+
content.join.html_safe
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def admin_nested_fields(method, options = {})
|
33
|
+
field_wrapper(method, options) do
|
34
|
+
fields_content = []
|
35
|
+
fields_content << @template.content_tag(:div, id: "#{method}_nested_form") do
|
36
|
+
nested_content = []
|
37
|
+
nested_content << self.fields_for(method.to_sym) do |ff|
|
38
|
+
@template.render("#{method.to_s.singularize}_fields", f: ff)
|
39
|
+
end
|
40
|
+
|
41
|
+
nested_content << @template.content_tag(:div, class: 'col-sm-12') do
|
42
|
+
btns_content = [@template.link_to_add_association('添加', self, method.to_sym, class: 'btn btn-sm btn-success'), " "]
|
43
|
+
btns_content << @template.button_tag(:input, class: "sort-btn btn btn-sm btn-success", type: "button", id: "#{method}-sort-btn") { "排序" }
|
44
|
+
btns_content.join.html_safe
|
45
|
+
end
|
46
|
+
|
47
|
+
nested_content.join.html_safe
|
48
|
+
end
|
49
|
+
|
50
|
+
fields_content << @template.render("admin/application/partials/sort_script",
|
51
|
+
sort_btn_id: "##{method}-sort-btn",
|
52
|
+
container_id: "##{method}_nested_form",
|
53
|
+
resource_parts: method.pluralize)
|
54
|
+
|
55
|
+
fields_content << @template.content_tag(:script) do
|
56
|
+
%(
|
57
|
+
$(document).on('turbolinks:load', function () {
|
58
|
+
Cocoon();
|
59
|
+
});
|
60
|
+
).html_safe
|
61
|
+
end
|
62
|
+
fields_content.join.html_safe
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def admin_render_field(method, options = {})
|
67
|
+
field_wrapper(method, options) do
|
68
|
+
@template.render("#{method.to_s.singularize}", f: self)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def admin_display_field(method, options = {})
|
73
|
+
field_wrapper(method, options) do
|
74
|
+
@template.content_tag(:span, record.try(method))
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def admin_text_field(method, options = {})
|
79
|
+
field_wrapper(method, options) do
|
80
|
+
@template.text_field(@object_name, method, objectify_options(options))
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def admin_password_field(method, options = {})
|
85
|
+
field_wrapper(method, options) do
|
86
|
+
@template.password_field(@object_name, method, objectify_options(options))
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def admin_number_field(method, options = {})
|
91
|
+
field_wrapper(method, options) do
|
92
|
+
@template.number_field(@object_name, method, objectify_options(options))
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def admin_generic_field(type, method, options = {})
|
97
|
+
field_wrapper(method, options) do
|
98
|
+
send type, @object_name, method, objectify_options(options)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def admin_file_field(method, options = {})
|
103
|
+
file_url = record.try(method)
|
104
|
+
file_url = file_url.class.superclass == CarrierWave::Uploader::Base ? file_url.try('url') : file_url
|
105
|
+
|
106
|
+
field_wrapper(method, options) do
|
107
|
+
content = [@template.file_field(@object_name, method, objectify_options(options))]
|
108
|
+
content << @template.link_to(file_url, target: '_blank') {"点击查看"}
|
109
|
+
content.join.html_safe
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def admin_image_field(method, options = {})
|
114
|
+
field_wrapper(method, options) do
|
115
|
+
img_url = record.try(method)
|
116
|
+
img_url = img_url.class.superclass == CarrierWave::Uploader::Base ? img_url.try('url') : img_url
|
117
|
+
content = [@template.link_to(img_url, target: '_blank') do
|
118
|
+
decorate.respond_to?("human_#{method}") ? decorate.human(method) : @template.image_tag(img_url)
|
119
|
+
end]
|
120
|
+
content << @template.file_field(@object_name, method, objectify_options(options))
|
121
|
+
content.join.html_safe
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def admin_collection_check_boxes(method, options = {})
|
126
|
+
collection, value_method = options.delete(:collection), options.delete(:value_method)
|
127
|
+
text_method, html_options = options.delete(:text_method), options.delete(:html_options) || {}
|
128
|
+
field_wrapper(method, options) do
|
129
|
+
@template.collection_check_boxes(@object_name, method, eval(collection), value_method, text_method, objectify_options(options), @default_options.merge(html_options)) do |b|
|
130
|
+
@template.content_tag(:span) { [b.check_box, b.text].join(' ').html_safe }
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def admin_collection_select2(method, options = {})
|
136
|
+
collection, value_method = options.delete(:collection), options.delete(:value_method)
|
137
|
+
text_method, html_options = options.delete(:text_method), options.delete(:html_options) || {}
|
138
|
+
is_array = options.delete(:is_array)
|
139
|
+
select2_options = options.delete(:select2_options)
|
140
|
+
options.merge!(prompt: true)
|
141
|
+
html_options.merge!(options.extract!(:label_class, :required, :block))
|
142
|
+
html_options.merge!(name: "#{@object_name}[#{method}][]") if is_array
|
143
|
+
sel_scripts = field_wrapper(method, html_options) do
|
144
|
+
@template.collection_select(@object_name, method, eval(collection), value_method, text_method, objectify_options(options), @default_options.merge(html_options))
|
145
|
+
end
|
146
|
+
select2_scripts = @template.content_tag(:script) { "$('##{input_dom_id(method)}').select2(#{select2_options.to_json});".html_safe }
|
147
|
+
sel_scripts.concat(select2_scripts)
|
148
|
+
end
|
149
|
+
|
150
|
+
def admin_collection_select(method, options = {})
|
151
|
+
collection, value_method = options.delete(:collection), options.delete(:value_method)
|
152
|
+
text_method, html_options = options.delete(:text_method), options.delete(:html_options) || {}
|
153
|
+
is_array = options.delete(:is_array)
|
154
|
+
options.merge!(prompt: true)
|
155
|
+
html_options.merge!(options.extract!(:label_class, :required, :block))
|
156
|
+
html_options.merge!(name: "#{@object_name}[#{method}][]") if is_array
|
157
|
+
field_wrapper(method, html_options) do
|
158
|
+
@template.collection_select(@object_name, method, eval(collection), value_method, text_method, objectify_options(options), @default_options.merge(html_options))
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def admin_text_area(method, options = {})
|
163
|
+
field_wrapper(method, options) do
|
164
|
+
@template.text_area(@object_name, method, objectify_options(options))
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def admin_date_select(method, options = {})
|
169
|
+
field_wrapper(method, options) do
|
170
|
+
options[:class] = [options[:class], 'datepicker'].flatten
|
171
|
+
@template.text_field(@object_name, method, objectify_options(options))
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def admin_datetime_select(method, options = {})
|
176
|
+
field_wrapper(method, options) do
|
177
|
+
options[:class] = [options[:class], 'datetimepicker'].flatten
|
178
|
+
@template.text_field(@object_name, method, objectify_options(options))
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def admin_collection_radio_buttons(method, options = {})
|
183
|
+
collection, value_method = options.delete(:collection), options.delete(:value_method)
|
184
|
+
text_method, html_options = options.delete(:text_method), options.delete(:html_options) || {}
|
185
|
+
field_wrapper(method, options) do
|
186
|
+
@template.collection_radio_buttons(@object_name, method, eval(collection), value_method, text_method, objectify_options(options), @default_options.merge(html_options)) do |b|
|
187
|
+
@template.content_tag(:span, style: 'height: 34px;display: inline-block;') { [b.radio_button, b.text].join(' ').html_safe }
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def build_form_for(decorator, *attribute_name_list)
|
193
|
+
fields_hash = []
|
194
|
+
attribute_name_list.each do |attribute_name|
|
195
|
+
fields_hash = decorator.try(attribute_name)
|
196
|
+
break if !fields_hash.empty?
|
197
|
+
end
|
198
|
+
if fields_hash.empty?
|
199
|
+
fields_hash = decorator.try(:new_attributes)
|
200
|
+
end
|
201
|
+
profiles = fields_hash.fetch(:profiles, [])
|
202
|
+
|
203
|
+
form_fields = if profiles.present?
|
204
|
+
profiles.collect { |profile|
|
205
|
+
fields = fields_hash.fetch(profile.to_sym, {})
|
206
|
+
content = []
|
207
|
+
content << @template.content_tag(:h3, I18n.t("profiles.#{profile}", default: profile), class: 'lead clearfix')
|
208
|
+
content << build_fields_for(decorator, fields)
|
209
|
+
content.join.html_safe
|
210
|
+
}.join.html_safe
|
211
|
+
else
|
212
|
+
build_fields_for(decorator, fields_hash)
|
213
|
+
end
|
214
|
+
[form_fields, build_form_buttons].join.html_safe
|
215
|
+
end
|
216
|
+
|
217
|
+
def build_fields_for(decorator, fields)
|
218
|
+
@template.content_tag(:ul, class: 'row list-unstyled') do
|
219
|
+
fields.each do |field_name|
|
220
|
+
options = decorator.try(:attribute_type_for, field_name, true) || {type: 'text_field', required: false}
|
221
|
+
type = options.delete(:type)
|
222
|
+
|
223
|
+
if self.respond_to?('admin_'.concat(type))
|
224
|
+
concat(send('admin_'.concat(type), field_name, options))
|
225
|
+
else
|
226
|
+
concat admin_generic_field(type, field_name, options)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
def build_form_buttons
|
233
|
+
lead = @template.content_tag(:h3, nil, class: 'lead clearfix')
|
234
|
+
tags = @template.content_tag(:div, [submit_tag, ' ', reset_tag].join.html_safe, class: 'col-sm-12')
|
235
|
+
lead.concat(@template.content_tag(:div, tags, class: 'row'))
|
236
|
+
end
|
237
|
+
|
238
|
+
def submit_tag
|
239
|
+
tag_text = @template.content_tag(:i, nil, class: 'glyphicon glyphicon-check')
|
240
|
+
tag_text.concat(@template.content_tag(:span, '提交'))
|
241
|
+
@template.content_tag(:button, tag_text, class: 'btn btn-icon btn-info', type: 'submit')
|
242
|
+
end
|
243
|
+
|
244
|
+
def reset_tag
|
245
|
+
tag_text = @template.content_tag(:i, nil, class: 'glyphicon glyphicon-repeat')
|
246
|
+
tag_text.concat(@template.content_tag(:span, '重置'))
|
247
|
+
@template.content_tag(:button, tag_text, class: 'btn btn-icon btn-warning', type: 'reset')
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Admin
|
2
|
+
class ApplicationController < ::ApplicationController
|
3
|
+
layout "admin"
|
4
|
+
|
5
|
+
include Admin::ActionController
|
6
|
+
include Admin::CommonActions
|
7
|
+
include ApplicationHelper
|
8
|
+
|
9
|
+
helper_method :scope, :model, :decorator, :record, :decorate, :records, :render_to_string
|
10
|
+
|
11
|
+
before_action :authenticate_user!
|
12
|
+
before_action do
|
13
|
+
if current_user.user? or current_user.role.blank?
|
14
|
+
render :file => "public/403.html", :status => :unauthorized, :layout => false
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
before_action do
|
19
|
+
prepend_view_path 'app/views/admin/application'
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,172 @@
|
|
1
|
+
class BaseDecorator < Draper::Decorator
|
2
|
+
extend Dry::Configurable
|
3
|
+
|
4
|
+
# 对应的model class
|
5
|
+
setting :model
|
6
|
+
|
7
|
+
DEFINITIONS = {
|
8
|
+
# 属性定义列表
|
9
|
+
attribute_types: {},
|
10
|
+
|
11
|
+
# index page用到的字段/筛选/搜索
|
12
|
+
collection_filters: [],
|
13
|
+
collection_search: {},
|
14
|
+
collection_attributes: [],
|
15
|
+
|
16
|
+
# new/edit page用到的字段列表
|
17
|
+
form_attributes: [],
|
18
|
+
edit_form_attributes: [],
|
19
|
+
|
20
|
+
# show page 用到的字段列表
|
21
|
+
show_page_attributes: [],
|
22
|
+
|
23
|
+
# class/instance 操作列表
|
24
|
+
permitted_class_methods: {},
|
25
|
+
permitted_instance_methods: {},
|
26
|
+
|
27
|
+
extra_permitted_attributes: {}
|
28
|
+
}.freeze
|
29
|
+
|
30
|
+
DEFINITIONS.each do |key, default_value|
|
31
|
+
setting key, default_value
|
32
|
+
delegate key, to: :class
|
33
|
+
end
|
34
|
+
|
35
|
+
def human(field)
|
36
|
+
value = try("human_#{field}") || try(field) || object.try("#{field}_text") || object.try(field)
|
37
|
+
value = field.split('.').inject(object) {
|
38
|
+
|result, method| result.try(method)
|
39
|
+
} if field.include?('.')
|
40
|
+
|
41
|
+
case value
|
42
|
+
when ActiveSupport::TimeWithZone
|
43
|
+
value.strftime('%Y-%m-%d %H:%M:%S')
|
44
|
+
when TrueClass
|
45
|
+
I18n.t("#{field}.true_html", default: '-').html_safe
|
46
|
+
when FalseClass
|
47
|
+
I18n.t("#{field}.false_html", default: '-').html_safe
|
48
|
+
else
|
49
|
+
value.presence || '-'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class << self
|
54
|
+
delegate :model, to: :config
|
55
|
+
|
56
|
+
def inherited(subclass)
|
57
|
+
super
|
58
|
+
|
59
|
+
subclass.configure do |config|
|
60
|
+
config.model = subclass.name.remove(/Decorator\Z/).singularize.safe_constantize
|
61
|
+
|
62
|
+
scope = config.model.model_name.singular
|
63
|
+
config.attribute_types = I18n.t(['attribute_types', scope].join('.'), default: {}).deep_dup
|
64
|
+
I18n.t(['decorator', scope].join('.'), default: {}).map { |key, value| config.send("#{key}=", value) }
|
65
|
+
|
66
|
+
config.model.columns_hash.each do |name, col|
|
67
|
+
name = name.to_sym
|
68
|
+
if config.attribute_types[name].nil?
|
69
|
+
if col.array
|
70
|
+
config.attribute_types[name] = {
|
71
|
+
type: 'collection_select',
|
72
|
+
collection: '[]',
|
73
|
+
text_method: :last,
|
74
|
+
value_method: :first,
|
75
|
+
}
|
76
|
+
else
|
77
|
+
case col.type
|
78
|
+
when 'integer', 'float'
|
79
|
+
config.attribute_types[name] = {
|
80
|
+
type: 'number_field',
|
81
|
+
required: false
|
82
|
+
}
|
83
|
+
when 'datetime'
|
84
|
+
config.attribute_types[name] = {
|
85
|
+
type: 'datetime_select',
|
86
|
+
required: false
|
87
|
+
}
|
88
|
+
when 'boolean'
|
89
|
+
config.attribute_types[name] = {
|
90
|
+
type: 'collection_check_boxes',
|
91
|
+
required: false
|
92
|
+
}
|
93
|
+
when 'date'
|
94
|
+
config.attribute_types[name] = {
|
95
|
+
type: 'date_select',
|
96
|
+
required: false
|
97
|
+
}
|
98
|
+
else
|
99
|
+
config.attribute_types[name] = {
|
100
|
+
type: 'text_field',
|
101
|
+
required: false
|
102
|
+
}
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# 设置对应的class
|
113
|
+
def decorate_to(klass)
|
114
|
+
config.model = klass
|
115
|
+
end
|
116
|
+
|
117
|
+
DEFINITIONS.keys.each do |method_name|
|
118
|
+
define_method(method_name) { |&block|
|
119
|
+
if block
|
120
|
+
config.send("#{method_name}=", block.call)
|
121
|
+
else
|
122
|
+
config.send(method_name)
|
123
|
+
end
|
124
|
+
}
|
125
|
+
end
|
126
|
+
|
127
|
+
# 获取某个属性的定义
|
128
|
+
def attribute_type_for(attribute_name, quiet = false)
|
129
|
+
config.attribute_types.fetch(attribute_name.to_sym) do
|
130
|
+
fail attribute_not_found_message(attribute_name) unless quiet
|
131
|
+
end.deep_dup
|
132
|
+
end
|
133
|
+
|
134
|
+
# 表单必填字段列表
|
135
|
+
def required_attributes
|
136
|
+
config.attribute_types.collect { |name, field| name if field[:required] }.compact
|
137
|
+
end
|
138
|
+
|
139
|
+
# 表单允许提交的参数
|
140
|
+
def permitted_attributes
|
141
|
+
_attributes = config.form_attributes
|
142
|
+
|
143
|
+
if _attributes.is_a?(Hash)
|
144
|
+
profiles = _attributes.fetch(:profiles, [])
|
145
|
+
_attributes = profiles.collect { |profile| _attributes.fetch(profile.to_sym, []) }.flatten.uniq
|
146
|
+
# _attributes = _attributes.each {|attr| _attributes << "#{attr}_attributes"}
|
147
|
+
end
|
148
|
+
|
149
|
+
array, hash = [], {}
|
150
|
+
_attributes.each do |field_name|
|
151
|
+
attribute_type_for(field_name)[:is_array] ? hash.merge!(field_name => []) : array.push(field_name)
|
152
|
+
end
|
153
|
+
|
154
|
+
[array, hash].append(config.extra_permitted_attributes)
|
155
|
+
#.append(filters: config.collection_filters.keys)
|
156
|
+
end
|
157
|
+
|
158
|
+
def permitted_class_methods_for(action_name)
|
159
|
+
config.permitted_class_methods.fetch(action_name.to_sym, [])
|
160
|
+
end
|
161
|
+
|
162
|
+
def permitted_instance_methods_for(action_name)
|
163
|
+
config.permitted_instance_methods.fetch(action_name.to_sym, [])
|
164
|
+
end
|
165
|
+
|
166
|
+
private
|
167
|
+
|
168
|
+
def attribute_not_found_message(attribute_name)
|
169
|
+
"Attribute #{attribute_name} could not be found in #{self}.attribute_types"
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|