carload 0.2.2 → 0.3.0

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: d65cff581f4e29c73cf1e4fe384de789b4b48bd8
4
- data.tar.gz: e080ef8a8a7118df7553ac7b565d80880a562ff7
3
+ metadata.gz: 8cd7d8e37f5576e78d936449c1e5037208db1789
4
+ data.tar.gz: feed52ae809a23d8cdc1f591e8ea76760e387945
5
5
  SHA512:
6
- metadata.gz: 7d565a9cd04ac9955051d1e41e5b789886345c8d3b7baefcd78cfc37728f90ebb816fda1f141edf21458b76c5772227a17b64faf66f7b1a54c6bb7984a821beb
7
- data.tar.gz: f1f945a17a018ae47f1780e8cbd6047449ac1580defef761aa3404f858b0cd15ea4fea6f8c73819ee18004b1f00c15c585a0d2206129a09be885e9f75e5a32b8
6
+ metadata.gz: cbc8dcf229e5789e2f7517b8b8c7e1b717abfc63372fd76bc537571969072a0f260d8cd5b90b56aba35ea5d5780f20a6b3a2cb7222552eb1828548ba528100d2
7
+ data.tar.gz: 4faa773c891477b8f1b72c9b54e33ecebdd55d8c9e333ced189a8acec895c24309d54bf4ae4ca14fd4133ef4075c5d74e92cf4d1aa78b52ce68ead454f862df8
@@ -11,3 +11,7 @@
11
11
  .select2-results__option--highlighted {
12
12
  background-color: $main-color !important;
13
13
  }
14
+
15
+ .select2-container {
16
+ min-width: 174px;
17
+ }
@@ -4,6 +4,7 @@
4
4
  //= require upload-image
5
5
 
6
6
  $spacing: 16px;
7
+ $half-spacing: 8px;
7
8
 
8
9
  @import 'bootstrap-sprockets';
9
10
  @import 'bootstrap';
@@ -42,17 +43,30 @@ $spacing: 16px;
42
43
 
43
44
  #main-container {
44
45
  width: 100%;
45
- padding-left: $spacing;
46
- padding-right: $spacing;
47
46
  #main-content {
48
47
  margin-top: 50px;
48
+ padding-left: $spacing;
49
+ padding-right: $spacing;
49
50
  }
50
51
  }
51
52
 
52
53
  .model-index-panel {
53
54
  width: 100%;
55
+ .panel-body {
56
+ overflow: scroll;
57
+ padding-left: 0;
58
+ padding-right: 0;
59
+ }
54
60
  table {
55
61
  margin-bottom: 0;
62
+ table-layout: fixed;
63
+ width: 100%;
64
+ td {
65
+ overflow: scroll;
66
+ }
67
+ th:last-child {
68
+ width: 50px;
69
+ }
56
70
  }
57
71
  }
58
72
  .model-new-panel, .model-edit-panel {
@@ -11,7 +11,7 @@ module Carload
11
11
  include Croppable
12
12
 
13
13
  def index
14
- authorize :dashboard, :index? unless Carload.auth_solution == :none
14
+ authorize :carload_dashboard, :index? unless Carload.auth_solution == :none
15
15
  @search = @model_class.search(params[:q])
16
16
  @objects = @search.result.page(params[:page])
17
17
  @show_attributes = Dashboard.model(@model_name).index_page[:shows][:attributes] + [:created_at, :updated_at]
@@ -20,16 +20,16 @@ module Carload
20
20
  end
21
21
 
22
22
  def new
23
- authorize :dashboard, :new? unless Carload.auth_solution == :none
23
+ authorize :carload_dashboard, :new? unless Carload.auth_solution == :none
24
24
  @object = @model_class.new
25
25
  end
26
26
 
27
27
  def edit
28
- authorize :dashboard, :edit? unless Carload.auth_solution == :none
28
+ authorize :carload_dashboard, :edit? unless Carload.auth_solution == :none
29
29
  end
30
30
 
31
31
  def create
32
- authorize :dashboard, :create? unless Carload.auth_solution == :none
32
+ authorize :carload_dashboard, :create? unless Carload.auth_solution == :none
33
33
  @object = @model_class.create model_params
34
34
  if @object.save
35
35
  redirect_to action: :index, model: @model_names # TODO: To show page.
@@ -39,7 +39,7 @@ module Carload
39
39
  end
40
40
 
41
41
  def update
42
- authorize :dashboard, :update? unless Carload.auth_solution == :none
42
+ authorize :carload_dashboard, :update? unless Carload.auth_solution == :none
43
43
  if @object.update model_params
44
44
  redirect_to action: :index, model: @model_names # TODO: To show page.
45
45
  else
@@ -48,7 +48,7 @@ module Carload
48
48
  end
49
49
 
50
50
  def destroy
51
- authorize :dashboard, :destroy? unless Carload.auth_solution == :none
51
+ authorize :carload_dashboard, :destroy? unless Carload.auth_solution == :none
52
52
  @object.destroy
53
53
  redirect_to action: :index, model: @model_names
54
54
  end
@@ -1,16 +1,63 @@
1
1
  module Carload
2
2
  module DashboardHelper
3
3
  def generate_input form, model_name, attribute_name, column
4
- if column.type == :integer and attribute_name =~ /_id/
5
- associated_model = attribute_name.sub('_id', '')
6
- label_attribute = Dashboard.model(model_name).associated_models[associated_model.to_sym]
7
- form.association associated_model,
8
- label_method: label_attribute,
9
- label: t("activerecord.models.#{associated_model}"),
4
+ if Dashboard::ModelSpec.foreign_key? attribute_name
5
+ associated_model = attribute_name.gsub(/_id$/, '').to_sym
6
+ options = Dashboard.model(model_name).associated_models[associated_model]
7
+ label_attribute = options[:choose_by]
8
+ if options[:polymorphic]
9
+ forms = ''
10
+ options[:available_models].each_with_index do |real_model, i|
11
+ forms << form.input(attribute_name,
12
+ label: t("activerecord.attributes.#{model_name}.#{real_model}.#{label_attribute}"),
13
+ collection: real_model.camelize.constantize.all,
14
+ label_method: label_attribute,
15
+ value_method: :id,
16
+ input_html: {
17
+ class: 'use-select2',
18
+ data: {
19
+ placeholder: t('carload.placeholder.select', thing: t("activerecord.attributes.#{real_model}.#{label_attribute}"))
20
+ }
21
+ },
22
+ wrapper_html: {
23
+ id: "#{real_model.camelize}-#{label_attribute}"
24
+ }
25
+ )
26
+ end
27
+ # Add JavaScript to select which real model to work on.
28
+ forms << <<-EOT
29
+ <script>
30
+ $('.#{model_name}_#{associated_model}_id').hide()
31
+ if ($('##{model_name}_#{associated_model}_type').val() != '') {
32
+ $('#' + $('##{model_name}_#{associated_model}_type').val() + '-#{label_attribute}').show()
33
+ }
34
+ $('##{model_name}_#{associated_model}_type').change(function() {
35
+ $('.#{model_name}_#{associated_model}_id').hide()
36
+ $('#' + $(this).val() + '-#{label_attribute}').show()
37
+ $('.package_packagable_id > .select2-container').css('width', '100%')
38
+ })
39
+ </script>
40
+ EOT
41
+ raw forms.html_safe
42
+ else
43
+ form.association associated_model,
44
+ label_method: label_attribute,
45
+ label: t("activerecord.models.#{associated_model}"),
46
+ input_html: {
47
+ class: 'use-select2',
48
+ data: {
49
+ placeholder: t('carload.placeholder.select', thing: t("activerecord.attributes.#{associated_model}.#{label_attribute}"))
50
+ }
51
+ }
52
+ end
53
+ elsif attribute_name =~ /_type$/
54
+ associated_model = attribute_name.gsub(/_type$/, '').to_sym
55
+ options = Dashboard.model(model_name).associated_models[associated_model]
56
+ form.input attribute_name, collection: options[:available_models].map(&:camelize),
10
57
  input_html: {
11
58
  class: 'use-select2',
12
59
  data: {
13
- placeholder: t('carload.placeholder.select', thing: t("activerecord.attributes.#{associated_model}.#{label_attribute}"))
60
+ placeholder: t('carload.placeholder.select', thing: t("activerecord.attributes.#{model_name}.#{attribute_name}"))
14
61
  }
15
62
  }
16
63
  elsif needs_upload?(model_name, attribute_name) and image?(attribute_name)
@@ -20,6 +67,23 @@ module Carload
20
67
  end
21
68
  end
22
69
 
70
+ def generate_search_input form, model_name, attribute
71
+ if attribute[:options]
72
+ form.input "#{attribute[:name].to_s.gsub('.', '_')}_#{attribute[:term]}",
73
+ required: false, label: false, collection: attribute[:options],
74
+ input_html: {
75
+ class: 'use-select2',
76
+ data: {
77
+ placeholder: t('carload.placeholder.select', thing: t("activerecord.attributes.#{model_name}.#{attribute[:name]}"))
78
+ }
79
+ }
80
+ else
81
+ form.input "#{attribute[:name].to_s.gsub('.', '_')}_#{attribute[:term]}",
82
+ placeholder: t("activerecord.attributes.#{@model_name}.#{attribute[:name]}"),
83
+ required: false, label: false
84
+ end
85
+ end
86
+
23
87
  def generate_show object, attribute
24
88
  case attribute
25
89
  when Symbol
@@ -1,4 +1,4 @@
1
- class DashboardPolicy < Struct.new(:user, :dashboard)
1
+ class CarloadDashboardPolicy < Struct.new(:user, :dashboard)
2
2
  [:index, :new, :edit, :destroy].each do |action|
3
3
  define_method :"#{action}?" do
4
4
  return true if Carload.auth_solution == :none
@@ -1,7 +1,7 @@
1
1
  <%= simple_form_for @object, url: "/carload/dashboard/#{@model_names}/#{@object.id}" do |f| %>
2
2
  <% @model_class.columns_hash.each do |name, column| %>
3
- <% next if ['id', 'created_at', 'updated_at'].include? name %>
4
- <%= generate_input f, @model_name, name, column %>
3
+ <% next if Dashboard::ModelSpec::SkippedAttributes.include? name %>
4
+ <%= generate_input(f, @model_name, name, column) rescue nil %>
5
5
  <% end %>
6
6
  <%= f.button :submit, t('carload.action.submit'), class: 'btn btn-primary' %>
7
7
  <% end %>
@@ -1,8 +1,7 @@
1
1
  <%= simple_form_for @search, url: dashboard_search_path(@model_names), method: :post do |f| %>
2
2
  <div class='search-fields'>
3
3
  <% @search_attributes.each do |attribute| %>
4
- <%= f.input "#{attribute[:name].to_s.gsub('.', '_')}_#{attribute[:term]}", required: false, label: false,
5
- placeholder: t("activerecord.attributes.#{@model_name}.#{attribute[:name]}") %>
4
+ <%= generate_search_input f, @model_name, attribute %>
6
5
  <% end %>
7
6
  </div>
8
7
  <div class='search-buttons'>
@@ -2,6 +2,7 @@
2
2
  <html>
3
3
  <head>
4
4
  <title>Carload</title>
5
+ <meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0'/>
5
6
  <%= stylesheet_link_tag 'carload/dashboard', media: 'all' %>
6
7
  <%= javascript_include_tag 'carload/dashboard' %>
7
8
  <%= csrf_meta_tags %>
@@ -1,4 +1,12 @@
1
1
  en:
2
+ date:
3
+ order:
4
+ - :year
5
+ - :month
6
+ - :day
7
+ month_names: [~, January, February, March, April, May, June, July, August, September, October, November, December]
8
+ abbr_month_names: [~, Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec]
9
+
2
10
  carload:
3
11
  actions: Actions
4
12
  action:
@@ -1,4 +1,12 @@
1
1
  zh-CN:
2
+ date:
3
+ order:
4
+ - :year
5
+ - :month
6
+ - :day
7
+ month_names: [~, 1月, 2月, 3月, 4月, 5月, 6月, 7月, 8月, 9月, 10月, 11月, 12月]
8
+ abbr_month_names: [~, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
9
+
2
10
  carload:
3
11
  actions: 操作
4
12
  action:
@@ -3,21 +3,110 @@ module Carload
3
3
  class ModelSpec
4
4
  attr_accessor :default, :attributes, :index_page, :associated_models
5
5
 
6
- def initialize
6
+ SkippedAttributes = [
7
+ 'id', 'created_at', 'updated_at',
8
+ 'encrypted_password', 'reset_password_token',
9
+ 'reset_password_sent_at', 'remember_created_at',
10
+ 'sign_in_count', 'current_sign_in_at',
11
+ 'last_sign_in_at', 'current_sign_in_ip',
12
+ 'last_sign_in_ip'
13
+ ].freeze
14
+
15
+ def self.foreign_key? attribute
16
+ attribute =~ /_id$/
17
+ end
18
+
19
+ def foreign_key? attribute
20
+ ModelSpec.foreign_key? attribute
21
+ end
22
+
23
+ def self.polymorphic? model_class, attribute
24
+ return false unless foreign_key? attribute
25
+ model_class.column_names.include? "#{attribute.to_s.gsub('_id', '')}_type"
26
+ end
27
+
28
+ def polymorphic? model_class, attribute
29
+ ModelSpec.polymorphic? model_class, attribute
30
+ end
31
+
32
+ def initialize model_class = nil
7
33
  @default = false
8
34
  @attributes = ExtendedHash.new
9
35
  @index_page = ExtendedHash.new
10
36
  @index_page[:shows] = ExtendedHash.new
11
37
  @index_page[:searches] = ExtendedHash.new
12
38
  @associated_models = {}
39
+ if model_class
40
+ @attributes[:permitted] = model_class.column_names - SkippedAttributes
41
+ @attributes[:permitted].each do |attribute|
42
+ @index_page[:shows][:attributes] ||= []
43
+ @index_page[:searches][:attributes] ||= []
44
+ if foreign_key? attribute
45
+ associated_model = attribute.gsub('_id', '')
46
+ @associated_models[associated_model] = {
47
+ choose_by: nil, # Wait for setting.
48
+ polymorphic: polymorphic?(model_class, attribute),
49
+ model: model_class.name.underscore.to_sym
50
+ }
51
+ else
52
+ @index_page[:shows][:attributes] << attribute
53
+ @index_page[:searches][:attributes] << { name: attribute.to_sym, term: :cont }
54
+ end
55
+ end
56
+ end
57
+ end
58
+
59
+ def changed? spec
60
+ not @attributes[:permitted] == spec.attributes[:permitted] or
61
+ not @index_page[:searches][:attributes] == spec.index_page[:searches][:attributes]
62
+ end
63
+
64
+ def revise_stage_1!
65
+ # Handle polymorphic associated models if necessary.
66
+ @associated_models.each do |associated_model, options|
67
+ next if not options or not options[:polymorphic]
68
+ ActiveRecord::Base.descendants.each do |model|
69
+ next if model.name == 'ApplicationRecord'
70
+ if not model.reflect_on_all_associations.map(&:name).select { |x| x.to_s =~ /\b(#{options[:model]}|#{options[:model].to_s.pluralize})\b/ }.empty?
71
+ options[:available_models] ||= []
72
+ options[:available_models] << model.name.underscore
73
+ if not options[:common_attributes]
74
+ options[:common_attributes] = model.column_names - SkippedAttributes
75
+ else
76
+ options[:common_attributes] = options[:common_attributes] & model.column_names
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ def revise_stage_2!
84
+ # Handle associated models if necessary.
85
+ @associated_models.each do |associated_model, options|
86
+ next if not options
87
+ if options[:polymorphic]
88
+ # There should be a <associated_model>_type already.
89
+ @index_page[:shows][:attributes] << "#{associated_model}.#{options[:choose_by]}"
90
+ @index_page[:searches][:attributes].each do |attribute|
91
+ next unless attribute[:name] == :"#{associated_model}_type"
92
+ attribute[:options] = options[:available_models]
93
+ end
94
+ else
95
+ @index_page[:searches][:attributes] << {
96
+ name: "#{associated_model}.#{options[:choose_by]}",
97
+ term: :cont
98
+ }
99
+ end
100
+ end
13
101
  end
14
102
  end
15
103
 
16
104
  class << self
17
105
  def model name, &block
106
+ name = name.to_sym
18
107
  if block_given?
19
108
  @@models ||= {}
20
- spec = ModelSpec.new
109
+ spec = @@models[name] || ModelSpec.new
21
110
  yield spec
22
111
  @@models[name] = spec
23
112
  else
@@ -25,11 +114,16 @@ module Carload
25
114
  end
26
115
  end
27
116
 
28
- def associate args
29
- model_a = args.keys.first
30
- model_b = args.values.first
31
- @@models[model_a].associated_models[model_b] = args[:choose_by]
32
- @@models[model_b].associated_models[model_a] = nil
117
+ def associate options
118
+ model_a = options.keys.first
119
+ model_b = options.values.first
120
+ options.shift
121
+ @@models[model_a] ||= ModelSpec.new
122
+ @@models[model_a].associated_models[model_b] = options
123
+ unless options[:polymorphic]
124
+ @@models[model_b] ||= ModelSpec.new
125
+ @@models[model_b].associated_models[model_a] = nil
126
+ end
33
127
  end
34
128
 
35
129
  def models
@@ -41,6 +135,36 @@ module Carload
41
135
  @@models.each do |name, spec|
42
136
  return @@default_model = name if spec.default
43
137
  end
138
+ @@default_model = @@models.keys.first
139
+ end
140
+
141
+ def write file_path
142
+ content = File.read("#{Carload::Engine.root}/lib/generators/carload/templates/dashboard.rb")
143
+ content.gsub!(/^end$/, '')
144
+ default = true
145
+ models.each do |name, spec|
146
+ content << <<-RUBY
147
+ model :#{name} do |spec|
148
+ spec.default = #{default}
149
+ spec.attributes.permitted = #{spec.attributes.permitted}
150
+ spec.index_page.shows.attributes = #{spec.index_page.shows.attributes}
151
+ spec.index_page.searches.attributes = #{spec.index_page.searches.attributes}
152
+ end
153
+ RUBY
154
+ default = false
155
+ next if spec.associated_models.empty?
156
+ spec.associated_models.each do |associated_model, options|
157
+ next if not options.class == Hash or not options[:choose_by]
158
+ content << <<-RUBY
159
+ associate(#{{ name.to_sym => associated_model.to_sym }.merge options})
160
+ RUBY
161
+ end
162
+ end
163
+ content << "end\n"
164
+ File.open('app/carload/dashboard.rb', 'w') do |file|
165
+ file.write content
166
+ file.close
167
+ end
44
168
  end
45
169
  end
46
170
  end
@@ -1,3 +1,3 @@
1
1
  module Carload
2
- VERSION = '0.2.2'
2
+ VERSION = '0.3.0'
3
3
  end
@@ -0,0 +1,45 @@
1
+ module Carload
2
+ class DashGenerator < Rails::Generators::NamedBase
3
+ source_root File.expand_path('../templates', __FILE__)
4
+
5
+ def change_dashboard_file
6
+ # Process model once atime.
7
+ model = file_name
8
+ model_specs = {}
9
+ Rails.application.eager_load! # It is necessary to load models manually.
10
+ ActiveRecord::Base.descendants.each do |model| # Rails 5 can use ApplicationRecord.
11
+ next if model.name == 'ApplicationRecord'
12
+ name = model.name.underscore
13
+ model_specs[name] = Dashboard::ModelSpec.new model
14
+ end
15
+ spec = model_specs[model]
16
+ if not spec.associated_models.empty?
17
+ spec.revise_stage_1!
18
+ cli = HighLine.new
19
+ cli.say "\nModel #{model} has associated with other models."
20
+ spec.associated_models.each do |associated_model, options|
21
+ spec.associated_models[associated_model][:choose_by] = cli.choose do |menu|
22
+ menu.prompt = "Choose the attribute of model #{associated_model} for choosing in #{model}? "
23
+ attributes = options[:polymorphic] ? options[:common_attributes] : model_specs[associated_model].attributes.permitted
24
+ attributes.each do |attribute|
25
+ next if attribute =~ /_id$/
26
+ menu.choice attribute.to_sym
27
+ end
28
+ end
29
+ end
30
+ spec.revise_stage_2!
31
+ end
32
+ # Check if model exists in dashboard file, but it may be changed.
33
+ begin
34
+ load 'app/carload/dashboard.rb'
35
+ rescue LoadError
36
+ Dashboard.models[model.to_sym] = spec
37
+ Dashboard.write 'app/carload/dashboard.rb'
38
+ end
39
+ if not Dashboard.models.keys.include? model.to_sym or Dashboard.model(model).changed? spec
40
+ Dashboard.models[model.to_sym] = spec
41
+ Dashboard.write 'app/carload/dashboard.rb'
42
+ end
43
+ end
44
+ end
45
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: carload
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Li Dong
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-10-01 00:00:00.000000000 Z
11
+ date: 2016-10-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -198,6 +198,20 @@ dependencies:
198
198
  - - "~>"
199
199
  - !ruby/object:Gem::Version
200
200
  version: '0.1'
201
+ - !ruby/object:Gem::Dependency
202
+ name: highline
203
+ requirement: !ruby/object:Gem::Requirement
204
+ requirements:
205
+ - - "~>"
206
+ - !ruby/object:Gem::Version
207
+ version: '1.7'
208
+ type: :runtime
209
+ prerelease: false
210
+ version_requirements: !ruby/object:Gem::Requirement
211
+ requirements:
212
+ - - "~>"
213
+ - !ruby/object:Gem::Version
214
+ version: '1.7'
201
215
  description: Carload is built with taste, and tries to be just right!
202
216
  email:
203
217
  - dongli.init@gmail.com
@@ -225,8 +239,7 @@ files:
225
239
  - app/jobs/carload/application_job.rb
226
240
  - app/mailers/carload/application_mailer.rb
227
241
  - app/models/carload/application_record.rb
228
- - app/policies/application_policy.rb
229
- - app/policies/dashboard_policy.rb
242
+ - app/policies/carload_dashboard_policy.rb
230
243
  - app/views/carload/dashboard/_form.html.erb
231
244
  - app/views/carload/dashboard/edit.html.erb
232
245
  - app/views/carload/dashboard/index.html.erb
@@ -258,6 +271,7 @@ files:
258
271
  - lib/carload/extended_hash.rb
259
272
  - lib/carload/version.rb
260
273
  - lib/generators/carload/USAGE
274
+ - lib/generators/carload/dash_generator.rb
261
275
  - lib/generators/carload/install_generator.rb
262
276
  - lib/generators/carload/templates/carload.rb
263
277
  - lib/generators/carload/templates/dashboard.rb
@@ -1,53 +0,0 @@
1
- class ApplicationPolicy
2
- attr_reader :user, :record
3
-
4
- def initialize(user, record)
5
- @user = user
6
- @record = record
7
- end
8
-
9
- def index?
10
- false
11
- end
12
-
13
- def show?
14
- scope.where(:id => record.id).exists?
15
- end
16
-
17
- def create?
18
- false
19
- end
20
-
21
- def new?
22
- create?
23
- end
24
-
25
- def update?
26
- false
27
- end
28
-
29
- def edit?
30
- update?
31
- end
32
-
33
- def destroy?
34
- false
35
- end
36
-
37
- def scope
38
- Pundit.policy_scope!(user, record.class)
39
- end
40
-
41
- class Scope
42
- attr_reader :user, :scope
43
-
44
- def initialize(user, scope)
45
- @user = user
46
- @scope = scope
47
- end
48
-
49
- def resolve
50
- scope
51
- end
52
- end
53
- end