rails-add_ons 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +28 -0
  4. data/Rakefile +24 -0
  5. data/app/assets/config/rails_add_ons_manifest.js +0 -0
  6. data/app/assets/javascripts/rails/add_ons/application/widgets.js.coffee +48 -0
  7. data/app/assets/javascripts/rails/add_ons/application.js +17 -0
  8. data/app/assets/javascripts/rails_add_ons.js +1 -0
  9. data/app/assets/stylesheets/rails/add_ons/application/bootstrap_overrides.scss +3 -0
  10. data/app/assets/stylesheets/rails/add_ons/application/table_monospaced.scss +3 -0
  11. data/app/assets/stylesheets/rails/add_ons/application/widgets.scss +5 -0
  12. data/app/assets/stylesheets/rails/add_ons/application.scss +4 -0
  13. data/app/assets/stylesheets/rails/add_ons/bootstrap_v3_flex_extension.scss +13 -0
  14. data/app/assets/stylesheets/rails/add_ons/font_awesome.css +3 -0
  15. data/app/assets/stylesheets/rails_add_ons.css +3 -0
  16. data/app/caches/api/cache.rb +24 -0
  17. data/app/components/component/base.rb +24 -0
  18. data/app/components/component/collection_table.rb +80 -0
  19. data/app/components/component/resource_table.rb +25 -0
  20. data/app/concerns/api_controller_concerns/exception_handling.rb +24 -0
  21. data/app/concerns/resources_controller/pagination.rb +15 -0
  22. data/app/concerns/resources_controller/sorting.rb +15 -0
  23. data/app/controllers/api/resources_controller/base.rb +254 -0
  24. data/app/controllers/api/service_controller/base.rb +73 -0
  25. data/app/controllers/rails/add_ons/widgets_controller.rb +10 -0
  26. data/app/controllers/resources_controller/base.rb +180 -0
  27. data/app/controllers/service_controller/base.rb +135 -0
  28. data/app/extensions/object_extensions.rb +8 -0
  29. data/app/helpers/rails/add_ons/table_helper.rb +50 -0
  30. data/app/helpers/rails/add_ons/widget_helper.rb +10 -0
  31. data/app/parsers/api/resources_controller/condition_parser.rb +65 -0
  32. data/app/services/rails/add_ons/service/base.rb +156 -0
  33. data/app/services/rails/add_ons/service/result/base.rb +25 -0
  34. data/app/views/component/_collection_table.haml +20 -0
  35. data/app/views/component/_resource_table.haml +15 -0
  36. data/app/views/frontend/_navbar.haml +23 -0
  37. data/app/views/layouts/rails/add_ons/application.haml +23 -0
  38. data/app/views/resources_controller/base/_after_show_table.haml +0 -0
  39. data/app/views/resources_controller/base/_before_show_table.haml +0 -0
  40. data/app/views/resources_controller/base/_form.haml +2 -0
  41. data/app/views/resources_controller/base/_form_buttons.haml +4 -0
  42. data/app/views/resources_controller/base/_form_errors.haml +6 -0
  43. data/app/views/resources_controller/base/_pagination.haml +4 -0
  44. data/app/views/resources_controller/base/_show.haml +2 -0
  45. data/app/views/resources_controller/base/_show_actions.haml +6 -0
  46. data/app/views/resources_controller/base/_table.haml +2 -0
  47. data/app/views/resources_controller/base/_table_actions.haml +12 -0
  48. data/app/views/resources_controller/base/edit.haml +6 -0
  49. data/app/views/resources_controller/base/index.haml +13 -0
  50. data/app/views/resources_controller/base/new.haml +6 -0
  51. data/app/views/resources_controller/base/show.haml +10 -0
  52. data/app/views/service_controller/base/_create_after_service_output.haml +0 -0
  53. data/app/views/service_controller/base/_create_before_service_output.haml +0 -0
  54. data/app/views/service_controller/base/_form_buttons.haml +2 -0
  55. data/app/views/service_controller/base/_form_errors.haml +6 -0
  56. data/app/views/service_controller/base/create.haml +14 -0
  57. data/app/views/service_controller/base/new.haml +6 -0
  58. data/app/views/widget/base/_remote_load_code.html.erb +4 -0
  59. data/app/views/widget/base/_widget_controls.haml +3 -0
  60. data/app/views/widget/render.js.erb +0 -0
  61. data/app/widgets/widget/base.rb +92 -0
  62. data/config/initializers/assets.rb +7 -0
  63. data/config/initializers/extend_object.rb +1 -0
  64. data/config/locales/de.yml +39 -0
  65. data/config/locales/en.yml +39 -0
  66. data/config/routes.rb +5 -0
  67. data/lib/rails/add_ons/engine.rb +7 -0
  68. data/lib/rails/add_ons/version.rb +5 -0
  69. data/lib/rails/add_ons.rb +7 -0
  70. data/lib/rails-add_ons.rb +8 -0
  71. data/lib/tasks/rails/add_ons_tasks.rake +4 -0
  72. metadata +268 -0
@@ -0,0 +1,73 @@
1
+ module Api
2
+ module ServiceController
3
+ class Base < ::ApiController
4
+ module RestActions
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ include ActionController::MimeResponds
9
+
10
+ respond_to :json
11
+
12
+ if respond_to?(:before_action)
13
+ before_action :initialize_service_for_create, only: [:create]
14
+ else
15
+ before_filter :initialize_service_for_create, only: [:create]
16
+ end
17
+ end
18
+
19
+ def create
20
+ @result = @service.perform
21
+ respond_to do |format|
22
+ if @result.success?
23
+ format.json { render json: serialize_result(@result), status: :created }
24
+ else
25
+ format.json { render json: { errors: serialize_errors(@result.errors) }, status: 422 }
26
+ end
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def initialize_service_for_create
33
+ # In rails 5 permitted_params is an instance of ActionController::Parameters.
34
+ # Stragely, when calling #delete on it, it does not delete the key, so we have
35
+ # to transform it into a hash first.
36
+ params_hash = permitted_params.try(:to_h).presence || permitted_params
37
+
38
+ options = params_hash.try(:delete, :options) || {}
39
+ @service = service_class.new(params_hash, options)
40
+ end
41
+
42
+ def permitted_params
43
+ raise "not implemented"
44
+ end
45
+ end
46
+
47
+ module Service
48
+ extend ActiveSupport::Concern
49
+
50
+ def service_class
51
+ self.class.service_class
52
+ end
53
+ end
54
+
55
+ module Serialization
56
+ private
57
+
58
+ def serialize_result(result)
59
+ result.as_json
60
+ end
61
+
62
+ def serialize_errors(errors)
63
+ errors
64
+ end
65
+ end
66
+
67
+ include RestActions
68
+ include Service
69
+ include Serialization
70
+ include ApiControllerConcerns::ExceptionHandling
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,10 @@
1
+ module Rails
2
+ module AddOns
3
+ class WidgetsController < ApplicationController
4
+ def remote_render
5
+ widget_name, action = *params[:name].split("#")
6
+ render plain: widget_name.camelize.constantize.new(view_context).render(action)
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,180 @@
1
+ module ResourcesController
2
+ class Base < ::FrontendController
3
+ layout 'rails/add_ons/application'
4
+
5
+ module RestActions
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ include ActionController::MimeResponds
10
+
11
+ respond_to :html
12
+ responders :flash
13
+
14
+ if respond_to?(:before_action)
15
+ before_action :load_collection, only: [:index]
16
+ before_action :load_resource, only: [:show, :edit, :destroy, :update]
17
+ before_action :initialize_resource, only: [:new]
18
+ before_action :initialize_resource_for_create, only: [:create]
19
+ else
20
+ before_filter :load_collection, only: [:index]
21
+ before_filter :load_resource, only: [:show, :edit, :destroy, :update]
22
+ before_filter :initialize_resource, only: [:new]
23
+ before_filter :initialize_resource_for_create, only: [:create]
24
+ end
25
+ end
26
+
27
+ def index; end
28
+ def new; end
29
+ def show; end
30
+ def edit; end
31
+
32
+ def update
33
+ if Rails::VERSION::MAJOR < 4
34
+ @resource.update_attributes(permitted_params)
35
+ else
36
+ @resource.update(permitted_params)
37
+ end
38
+ respond_with @resource
39
+ end
40
+
41
+ def destroy
42
+ @resource.destroy
43
+ respond_with @resource
44
+ end
45
+
46
+ def create
47
+ @resource.save
48
+ respond_with @resource
49
+ end
50
+
51
+ private
52
+
53
+ def after_create_location
54
+ ->(controller) { resource_path(@resource) }
55
+ end
56
+
57
+ def load_collection_scope
58
+ resource_class
59
+ end
60
+ def load_collection
61
+ @collection = load_collection_scope.all
62
+ end
63
+
64
+ def load_resource_scope
65
+ resource_class
66
+ end
67
+ def load_resource
68
+ @resource = load_resource_scope.find(params[:id])
69
+ end
70
+
71
+ def initialize_resource
72
+ @resource = resource_class.new
73
+ end
74
+
75
+ def initialize_resource_for_create
76
+ @resource = resource_class.new(permitted_params)
77
+ end
78
+
79
+ def permitted_params
80
+ raise "not implemented"
81
+ end
82
+ end
83
+
84
+ module Resources
85
+ extend ActiveSupport::Concern
86
+
87
+ included do
88
+ helper_method :resource_class
89
+ end
90
+
91
+ def resource_class
92
+ self.class.resource_class
93
+ end
94
+ end
95
+
96
+ module RestResourceUrls
97
+ extend ActiveSupport::Concern
98
+
99
+ included do
100
+ helper_method :new_resource_path
101
+ helper_method :collection_path
102
+ helper_method :resource_path
103
+ helper_method :edit_resource_path
104
+ end
105
+
106
+ private
107
+
108
+ def new_resource_path
109
+ url_for(action: :new, only_path: true)
110
+ end
111
+
112
+ def collection_path
113
+ url_for(action: :index, only_path: true)
114
+ end
115
+
116
+ def resource_path(resource)
117
+ url_for(action: :show, id: resource, only_path: true)
118
+ end
119
+
120
+ def edit_resource_path(resource)
121
+ url_for(action: :edit, id: resource, only_path: true)
122
+ end
123
+ end
124
+
125
+ module ResourceInflections
126
+ extend ActiveSupport::Concern
127
+
128
+ included do
129
+ helper_method :inflections
130
+ end
131
+
132
+ private
133
+
134
+ def inflections
135
+ {
136
+ resource_name: resource_class.model_name.human(count: 1),
137
+ collection_name: resource_class.model_name.human(count: 2)
138
+ }
139
+ end
140
+ end
141
+
142
+ module LocationHistory
143
+ extend ActiveSupport::Concern
144
+
145
+ included do
146
+ if respond_to?(:before_action)
147
+ before_action :store_location
148
+ else
149
+ before_filter :store_location
150
+ end
151
+ end
152
+
153
+ private
154
+
155
+ def store_location
156
+ truncate_location_history(9)
157
+ location_history[Time.zone.now] = request.referer
158
+ end
159
+
160
+ def location_history
161
+ session[:location_history] ||= {}
162
+ end
163
+
164
+ def last_location
165
+ location_history.sort.last.try(:last)
166
+ end
167
+
168
+ def truncate_location_history(count = 0)
169
+ return if location_history.size <= count
170
+ session[:location_history] = session[:location_history].sort.last(count).to_h
171
+ end
172
+ end
173
+
174
+ include RestActions
175
+ include Resources
176
+ include RestResourceUrls
177
+ include ResourceInflections
178
+ include LocationHistory
179
+ end
180
+ end
@@ -0,0 +1,135 @@
1
+ module ServiceController
2
+ class Base < ::FrontendController
3
+ layout 'rails/add_ons/application'
4
+
5
+ module RestActions
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ include ActionController::MimeResponds
10
+
11
+ respond_to :html
12
+ responders :flash
13
+
14
+ if respond_to?(:before_action)
15
+ before_action :initialize_service, only: [:new]
16
+ before_action :initialize_service_for_create, only: [:create]
17
+ else
18
+ before_filter :initialize_service, only: [:new]
19
+ before_filter :initialize_service_for_create, only: [:create]
20
+ end
21
+ end
22
+
23
+
24
+ def new; end
25
+
26
+ def create
27
+ @response = @resource.perform
28
+ if @response.success?
29
+ render :create
30
+ else
31
+ render :new
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def initialize_service
38
+ @resource = service_class.new
39
+ end
40
+
41
+ def initialize_service_for_create
42
+ @resource = service_class.new(permitted_params)
43
+ end
44
+
45
+ def permitted_params
46
+ raise "not implemented"
47
+ end
48
+ end
49
+
50
+ module Service
51
+ extend ActiveSupport::Concern
52
+
53
+ included do
54
+ helper_method :service_class
55
+ end
56
+
57
+ def service_class
58
+ self.class.service_class
59
+ end
60
+ end
61
+
62
+ module RestResourceUrls
63
+ extend ActiveSupport::Concern
64
+
65
+ included do
66
+ helper_method :new_resource_path
67
+ helper_method :create_resource_path
68
+ end
69
+
70
+ private
71
+
72
+ def new_resource_path
73
+ url_for(action: :new, only_path: true)
74
+ end
75
+
76
+ def create_resource_path
77
+ url_for(action: :create, only_path: true)
78
+ end
79
+ end
80
+
81
+ module ResourceInflections
82
+ extend ActiveSupport::Concern
83
+
84
+ included do
85
+ helper_method :inflections
86
+ end
87
+
88
+ private
89
+
90
+ def inflections
91
+ {
92
+ service_name: service_class.model_name.human
93
+ }
94
+ end
95
+ end
96
+
97
+ module LocationHistory
98
+ extend ActiveSupport::Concern
99
+
100
+ included do
101
+ if respond_to?(:before_action)
102
+ before_action :store_location
103
+ else
104
+ before_filter :store_location
105
+ end
106
+ end
107
+
108
+ private
109
+
110
+ def store_location
111
+ truncate_location_history(9)
112
+ location_history[Time.zone.now] = request.referer
113
+ end
114
+
115
+ def location_history
116
+ session[:location_history] ||= {}
117
+ end
118
+
119
+ def last_location
120
+ location_history.sort.last.try(:last)
121
+ end
122
+
123
+ def truncate_location_history(count = 0)
124
+ return if location_history.size <= count
125
+ session[:location_history] = session[:location_history].sort.last(count).to_h
126
+ end
127
+ end
128
+
129
+ include Service
130
+ include RestActions
131
+ include RestResourceUrls
132
+ include ResourceInflections
133
+ include LocationHistory
134
+ end
135
+ end
@@ -0,0 +1,8 @@
1
+ module ObjectExtensions
2
+ def try_all(*methods)
3
+ methods.map(&:to_sym).each do |method|
4
+ next unless respond_to?(method)
5
+ return send(method)
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,50 @@
1
+ module Rails
2
+ module AddOns
3
+ module TableHelper
4
+ def collection_table(options = {}, &block)
5
+ Component::CollectionTable.new(self, options, &block).perform
6
+ end
7
+
8
+ def resource_table(options = {}, &block)
9
+ Component::ResourceTable.new(self, options, &block).perform
10
+ end
11
+
12
+ def sort_link(column_name, title, options = {})
13
+ return title if options === false
14
+ SortLink.new(self, column_name, title, options).perform
15
+ end
16
+
17
+ class SortLink
18
+ def initialize(view_context, column_name, title, options)
19
+ default_options = {}
20
+
21
+ if options === true
22
+ @options = default_options
23
+ else
24
+ @options = options.reverse_merge(default_options)
25
+ end
26
+
27
+ @view_context = view_context
28
+ @column_name = column_name
29
+ @title = title
30
+
31
+ if h.params[:sort_direction].present?
32
+ @sort_direction = (h.params[:sort_direction].to_sym == :asc) ? :desc : :asc
33
+ else
34
+ @sort_direction = :asc
35
+ end
36
+ end
37
+
38
+ def perform
39
+ h.link_to(@title, h.url_for(sort_by: @column_name, sort_direction: @sort_direction))
40
+ end
41
+
42
+ private
43
+
44
+ def h
45
+ @view_context
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,10 @@
1
+ module Rails
2
+ module AddOns
3
+ module WidgetHelper
4
+ def render_widget(name, options = {}, &block)
5
+ klass = "#{name}_widget".camelize.classify.constantize
6
+ klass.new(self, options, &block).render
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,65 @@
1
+ module Api
2
+ module ResourcesController
3
+ class ConditionParser
4
+ OPERATOR_MAP = {
5
+ gt: :>,
6
+ gt_or_eq: :>=,
7
+ eq: :'=',
8
+ not_eq: :'<>',
9
+ lt_or_eq: :<=,
10
+ lt: :<
11
+ }
12
+
13
+ def initialize(field, condition)
14
+ # @condition = { field => condition }
15
+ @field, @condition = field, condition
16
+ end
17
+
18
+ def condition_statement
19
+ build_condition_statement(@field, @condition)
20
+ end
21
+
22
+ private
23
+
24
+ def build_condition_statement(parent_key, condition, nested = false)
25
+ if is_a_condition?(parent_key) && !nested
26
+ column, operator = extract_column_and_operator(parent_key)
27
+ ["#{column} = ?", condition]
28
+ else
29
+ if nested
30
+ column = extract_column(parent_key)
31
+ { column => condition }
32
+ else
33
+ { parent_key => build_condition_statement(condition.first[0], condition.first[1], true) }
34
+ end
35
+ end
36
+ end
37
+
38
+ def is_a_condition?(obj)
39
+ !!extract_operator(obj)
40
+ end
41
+
42
+ def extract_operator(obj)
43
+ string = obj.to_s
44
+ operator_map.each do |key, value|
45
+ return value if string.end_with?("(#{key})")
46
+ end
47
+ nil
48
+ end
49
+
50
+ def extract_column(obj)
51
+ obj.to_s.split("(").first
52
+ end
53
+
54
+ def extract_column_and_operator(string)
55
+ if string =~ /([a-z_]{1,})\(([a-z_]{2,})\)/
56
+ return $~[1], $~[2]
57
+ end
58
+ end
59
+
60
+ def operator_map
61
+ OPERATOR_MAP
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,156 @@
1
+ module Rails
2
+ module AddOns
3
+ module Service
4
+ class Base
5
+ include ActiveModel::Model
6
+
7
+ def self.attr_accessor(*args)
8
+ super
9
+ add_attribute_names(*args)
10
+ end
11
+
12
+ def self.add_attribute_names(*args)
13
+ args.each do |attr_name|
14
+ attribute_names << attr_name
15
+ end
16
+ end
17
+
18
+ def self.attribute_names
19
+ (@attr_names ||= [])
20
+ end
21
+
22
+ def self.i18n_scope
23
+ :activerecord
24
+ end
25
+
26
+ def self.call(*args)
27
+ new(*args).perform
28
+ end
29
+
30
+ def initialize(attributes = {}, options = {}, &block)
31
+ @options = options
32
+ @block = block
33
+ @attributes = {}
34
+ set_attributes(attributes)
35
+ initialize_result
36
+ initialize_errors
37
+ initialize_messages
38
+ after_initialize
39
+ end
40
+
41
+ private
42
+
43
+ module Attributes
44
+ def set_attributes(attributes)
45
+ attributes.each do |key, value|
46
+ send("#{key}=", value)
47
+ end
48
+ end
49
+ end
50
+
51
+ include Attributes
52
+
53
+ module Callbacks
54
+ def after_initialize; end
55
+ def before_perform; end
56
+ def after_perform; end
57
+ def before_validation; end
58
+ def after_validation; end
59
+ def after_perform; end
60
+
61
+ def perform
62
+ before_validation
63
+ return perform_result unless valid?
64
+ after_validation
65
+ before_perform
66
+ say "Performing" do
67
+ _perform
68
+ end
69
+ after_perform
70
+ perform_result
71
+ end
72
+ end
73
+
74
+ module Resultable
75
+ private
76
+
77
+ def initialize_result
78
+ @result = result_class.new
79
+ end
80
+
81
+ def perform_result
82
+ copy_messages_to_result
83
+ copy_errors_to_result
84
+ @result
85
+ end
86
+
87
+ def result_class
88
+ "#{self.class.name}::Result".constantize
89
+ end
90
+ end
91
+
92
+ module Errors
93
+ private
94
+
95
+ def initialize_errors
96
+ @errors = ActiveModel::Errors.new(self)
97
+ end
98
+
99
+ def copy_errors_to_result
100
+ @result.instance_variable_set(:@errors, @errors)
101
+ end
102
+
103
+ def copy_errors_from_to(obj, key_prefix)
104
+ obj.errors.each do |key, message|
105
+ @errors.add(key_prefix, message)
106
+ end
107
+ end
108
+ end
109
+
110
+ module Messages
111
+ private
112
+
113
+ def initialize_messages
114
+ @messages = []
115
+ end
116
+
117
+ def say(what, &block)
118
+ @indent ||= 0
119
+ if block_given?
120
+ @indent += 1
121
+ output "#{output_prefix}#{(" " * @indent)}#{what}..."
122
+ block_result = yield
123
+ say_done
124
+ @indent -= 1
125
+ block_result
126
+ else
127
+ output "#{output_prefix}#{(" " * @indent)}#{what}"
128
+ end
129
+ end
130
+
131
+ def say_done
132
+ say " => Done"
133
+ end
134
+
135
+ def output_prefix
136
+ "[#{self.class.name}] "
137
+ end
138
+
139
+ def output(what)
140
+ @messages << what
141
+ puts what
142
+ end
143
+
144
+ def copy_messages_to_result
145
+ @result.instance_variable_set(:@messages, @messages)
146
+ end
147
+ end
148
+
149
+ include Errors
150
+ include Resultable
151
+ include Callbacks
152
+ include Messages
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,25 @@
1
+ module Rails
2
+ module AddOns
3
+ module Service::Result
4
+ class Base
5
+ attr_reader :messages, :errors
6
+
7
+ module Succeedable
8
+ def success?
9
+ !failed?
10
+ end
11
+
12
+ def failed?
13
+ @errors.any?
14
+ end
15
+
16
+ def ok?
17
+ success?
18
+ end
19
+ end
20
+
21
+ include Succeedable
22
+ end
23
+ end
24
+ end
25
+ end