rails-add_ons 0.2.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.
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