edit_in_place 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +55 -32
- data/Rakefile +6 -9
- data/lib/edit_in_place.rb +15 -23
- data/lib/edit_in_place/array_definition.rb +26 -0
- data/lib/edit_in_place/builder.rb +136 -63
- data/lib/edit_in_place/configuration.rb +13 -4
- data/lib/edit_in_place/error.rb +16 -0
- data/lib/edit_in_place/extended_builder.rb +23 -2
- data/lib/edit_in_place/field_options.rb +9 -15
- data/lib/edit_in_place/field_type.rb +7 -8
- data/lib/edit_in_place/field_type_registrar.rb +9 -5
- data/lib/edit_in_place/middleware_registrar.rb +22 -0
- data/lib/edit_in_place/middleware_wrapper.rb +56 -0
- data/lib/edit_in_place/registrar.rb +12 -8
- data/lib/edit_in_place/unregistered_middleware_error.rb +13 -0
- data/lib/edit_in_place/version.rb +1 -1
- metadata +23 -26
- data/lib/edit_in_place/railtie.rb +0 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 98f6c29f5c933c2f0051d9199089dc41125fe9cf6bdf8d3f61e7a6718b828a40
|
4
|
+
data.tar.gz: 95f123e6fcacbd5b8a42489d9dea077d0b381f482d1cdb152245f61dd56071da
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 50f8954118b90962787e4131feae133a5babe443a4bdb36231c0a6c3fcbadfdc0f65d71831d8c449a0d9f07afc7f026598a8119088bc9bf1100e3a2a7da7658c
|
7
|
+
data.tar.gz: 5a66d2fbdbbc040ef95acaaa29f61aa886d8172baf9b298e44ae5f61ab2ffed669eb0e9113bd361babbe2bf85dcec0b00729a7deb536b80a3a878bb3a30e8fef
|
data/README.md
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
# EditInPlace
|
2
2
|
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/edit_in_place.svg)](https://badge.fury.io/rb/edit_in_place)
|
3
4
|
[![Build Status](https://travis-ci.com/jacoblockard99/edit_in_place.svg?branch=master)](https://travis-ci.com/jacoblockard99/edit_in_place)
|
4
5
|
[![Inline docs](http://inch-ci.org/github/jacoblockard99/edit_in_place.svg?branch=master)](http://inch-ci.org/github/jacoblockard99/edit_in_place)
|
5
6
|
[![Maintainability](https://api.codeclimate.com/v1/badges/389fc591cd9cccb2ceb1/maintainability)](https://codeclimate.com/github/jacoblockard99/edit_in_place/maintainability)
|
6
7
|
[![Test Coverage](https://api.codeclimate.com/v1/badges/389fc591cd9cccb2ceb1/test_coverage)](https://codeclimate.com/github/jacoblockard99/edit_in_place/test_coverage)
|
7
8
|
|
8
|
-
`edit_in_place` is a
|
9
|
+
`edit_in_place` is a Ruby gem that facilitates the creation of user interfaces that allow the user to edit content "in place" in a natural way. `edit_in_place` aims to be:
|
9
10
|
- **Flexible.** Everything has been designed with extensibility in mind. The `edit_in_place` core (this repository) can be extended for a huge variety of use cases.
|
10
11
|
- **Reliable.** Every aspect of the plugin is thoroughly tested and documented.
|
11
12
|
- **Natural.** Coding with `edit_in_place` is just as natural as editing content with it is! We think you'll really enjoy working with the `edit_in_place` API.
|
@@ -34,7 +35,7 @@ Or you may install it manually with:
|
|
34
35
|
$ gem install edit_in_place
|
35
36
|
```
|
36
37
|
|
37
|
-
`edit_in_place` currently has two dependencies: `
|
38
|
+
`edit_in_place` currently has two dependencies: `activesupport` and `middlegem`. The only extension required from `activesupport` is `Hash#deep_merge`.
|
38
39
|
|
39
40
|
## Concepts
|
40
41
|
|
@@ -65,12 +66,12 @@ Here are a few examples:
|
|
65
66
|
class TextFieldType < EditInPlace::FieldType
|
66
67
|
protected
|
67
68
|
|
68
|
-
def render_viewing(
|
69
|
-
|
69
|
+
def render_viewing(mode, name, value)
|
70
|
+
"<p>#{value}</p>"
|
70
71
|
end
|
71
72
|
|
72
|
-
def render_editing(
|
73
|
-
|
73
|
+
def render_editing(mode, name, value)
|
74
|
+
"<input type='text' value='#{value}' name='#{name]' />"
|
74
75
|
end
|
75
76
|
end
|
76
77
|
```
|
@@ -79,13 +80,13 @@ end
|
|
79
80
|
# boolean_field_type.rb
|
80
81
|
|
81
82
|
class DummyFieldtype < EditInPlace::FieldType
|
82
|
-
def render(
|
83
|
-
"You are currently #{
|
83
|
+
def render(mode, *)
|
84
|
+
"You are currently #{mode} the webpage!"
|
84
85
|
end
|
85
86
|
end
|
86
87
|
```
|
87
88
|
|
88
|
-
Notice that the `render` method is passed
|
89
|
+
Notice that the `render` method is passed a mode parameter. Also note how `render_viewing` and `render_editing` will be called according to the current mode. If you want to define a `render_*` method for a different mode, you'll need to override the `supported_modes` method, like so:
|
89
90
|
|
90
91
|
```ruby
|
91
92
|
class LockedField
|
@@ -109,7 +110,7 @@ class LockedField
|
|
109
110
|
end
|
110
111
|
```
|
111
112
|
|
112
|
-
Attempts to call `render` with a mode that is not
|
113
|
+
Attempts to call `render` with a mode that is not in `supported_modes` will result in an `EditInPlace::UnsupportedModeError`, even if the field type has a corresponding `render_*` method.
|
113
114
|
|
114
115
|
As mentioned earlier, one of the aims of `edit_in_place` is to be natural for the developer. Thus, in the interests of convenience, `edit_in_place` allows you to "register" field types with a name, making them easier to use. We might register our "text" field type, for example, like this:
|
115
116
|
|
@@ -133,6 +134,25 @@ Becomes:
|
|
133
134
|
|
134
135
|
Note that field type names must be symbols—strings are not allowed. Also note that duplicate registrations are not alowed and will raise an `EditInPlace::DuplicateRegistrationError`.
|
135
136
|
|
137
|
+
Another convenient feature is that you may use or register field type _classes_ whose
|
138
|
+
constructor has no parameters. For example:
|
139
|
+
|
140
|
+
```ruby
|
141
|
+
@builder.config.field_types.register :image, ImageFieldType
|
142
|
+
```
|
143
|
+
|
144
|
+
```ruby
|
145
|
+
<%= @builder.field TextFieldType, 'contact_name', 'Jacob' %>
|
146
|
+
<%= @builder.field :image, 'contact_name', 'Jacob' %>
|
147
|
+
```
|
148
|
+
|
149
|
+
Equivalent to:
|
150
|
+
|
151
|
+
```ruby
|
152
|
+
<%= @builder.field TextFieldType.new, 'contact_name', 'Jacob' %>
|
153
|
+
<%= @builder.field ImageFieldType.new, 'contact_name', 'Jacob' %>
|
154
|
+
```
|
155
|
+
|
136
156
|
### Configuring a Builder
|
137
157
|
|
138
158
|
The next step is creating and using an `EditInPlace::Builder` instance. A builder always has an `EditInPlace::Configuration` instance that contains all its options and context. When a builder is first instantiated, its configuration is copied from the global `EditInPlace` configuration. Thus, you can set any global configuration options using `EditInPlace.config` or `EditInPlace.configure`, and all builders with use those options by default. For example:
|
@@ -152,27 +172,6 @@ This would make editing the *default* mode. Then, you can modify builder-specifi
|
|
152
172
|
|
153
173
|
Both `EditInPlace.config` and `Builder#config` are `EditInPlace::Configuration` instances, so you configure them identically. You are encouraged to check out the [docs](https://rubydoc.info/github/jacoblockard99/edit_in_place/EditInPlace/Configuration) on `Configuration` to see all the available configuration options.
|
154
174
|
|
155
|
-
Only two options are really critical to using the builder: the view context and the mode. The view context is necessary when the field type needs access to a view context to render the field. In our `TextFieldType` example above, for example, the `text_field_tag` method was required. Probably the easiest way to pass the view context is to simply use the `view_context` method in Rails controllers. For example:
|
156
|
-
|
157
|
-
```
|
158
|
-
class SomeController
|
159
|
-
def index
|
160
|
-
@builder = Builder.new
|
161
|
-
@builder.field_options.view = view_context
|
162
|
-
end
|
163
|
-
end
|
164
|
-
```
|
165
|
-
|
166
|
-
Now, field types will automatically have access to the view context. This method has some pitfalls, however, most importantly that `view_context` returns a _new_ view context, not the one used in the actual view. If you must have the actual view context object, something like this could be done instead:
|
167
|
-
|
168
|
-
```erb
|
169
|
-
<% @builder.field_options.view = self %>
|
170
|
-
|
171
|
-
<%= @builder.field :text, "hello, world" %>
|
172
|
-
```
|
173
|
-
|
174
|
-
Of course, you would need to put this at the top of all your views.
|
175
|
-
|
176
175
|
There are a few approaches to managing builder modes. One approach is to have a seperate controller for each, like so:
|
177
176
|
|
178
177
|
```ruby
|
@@ -220,6 +219,12 @@ If your field type happens to expect the first input argument to be a hash, you
|
|
220
219
|
<%= @builder.field :some_type, {}, { option: true }, 'etc' %>
|
221
220
|
```
|
222
221
|
|
222
|
+
`Builder` also overrides `method_missing` to make it easier to call registered fields. If, for example, you have registered a `:text` field type, you can do the following:
|
223
|
+
|
224
|
+
```erb
|
225
|
+
<%= @builder.text_field 'phone_number', '(123) 456-7890' %>
|
226
|
+
```
|
227
|
+
|
223
228
|
### Middlewares
|
224
229
|
|
225
230
|
Perhaps the most powerful feature of `edit_in_place` are the field middlewares. `edit_in_place` uses [`middlegem`](https://github/jacoblockard99/middlegem) for middlewares, which you may want to briefly review. Field middlewares allow the inputs to a field to be transformed before actually making it to the field type's `render` method. The use cases for this are limitless.
|
@@ -254,6 +259,16 @@ end
|
|
254
259
|
|
255
260
|
Now, when viewing the site, the first one would show "(5000)" and the second would show "(a string)". Note that middlewares are often too verbose to be easily used like this. They are really best used by edit_in_place extensions.
|
256
261
|
|
262
|
+
Like field types, middlewares can also be registered, using `Configuration#registered_middlewares`. For example:
|
263
|
+
|
264
|
+
```ruby
|
265
|
+
@builder = Builder.new
|
266
|
+
@builder.configure do |c|
|
267
|
+
c.registered_middlewares.register :parentheses, ParenthesesMiddleware
|
268
|
+
c.field_options.middlewares << :parentheses
|
269
|
+
```
|
270
|
+
|
271
|
+
|
257
272
|
### Scoped Builders
|
258
273
|
|
259
274
|
There will likely be times when you wish for many fields to have the same `FieldOptions`. This can be accomplished with the `Builder#scoped` method, which allows field options to be shared across a block. For example:
|
@@ -264,7 +279,15 @@ There will likely be times when you wish for many fields to have the same `Field
|
|
264
279
|
<% end %>
|
265
280
|
```
|
266
281
|
|
267
|
-
Now any fields generated with the scoped `b` builder will have an `'example'` argument appended to their input (assuming that `AppendArgumentMiddleware` has been defined).
|
282
|
+
Now any fields generated with the scoped `b` builder will have an `'example'` argument appended to their input (assuming that `AppendArgumentMiddleware` has been defined). In fact, scoping a builder with middleware is comon enough that `Builder` also provides a `with_middlewares` convenience method:
|
283
|
+
|
284
|
+
```erb
|
285
|
+
<%= @builder.with_middlewares AppendArgumentMiddleware.new('example') |b| do %>
|
286
|
+
<%= b.field :text, 'name', 'value' %>
|
287
|
+
<% end %>
|
288
|
+
```
|
289
|
+
|
290
|
+
Note that `Builder#scope` and `Builder#middleware_scope` are aliases for `Builder#scoped` and `Builder#with_middlewares`, respectively.
|
268
291
|
|
269
292
|
### Extending Builder
|
270
293
|
|
data/Rakefile
CHANGED
@@ -1,15 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'bundler/setup'
|
4
|
-
|
5
3
|
require 'bundler/gem_tasks'
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
|
6
|
+
RSpec::Core::RakeTask.new(:spec)
|
6
7
|
|
7
|
-
require '
|
8
|
+
require 'rubocop/rake_task'
|
8
9
|
|
9
|
-
|
10
|
-
t.libs << 'test'
|
11
|
-
t.pattern = 'test/**/*_test.rb'
|
12
|
-
t.verbose = false
|
13
|
-
end
|
10
|
+
RuboCop::RakeTask.new
|
14
11
|
|
15
|
-
task default:
|
12
|
+
task default: %i[spec rubocop]
|
data/lib/edit_in_place.rb
CHANGED
@@ -1,14 +1,24 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require 'edit_in_place/
|
4
|
-
require 'edit_in_place/railtie'
|
3
|
+
require 'edit_in_place/array_definition'
|
5
4
|
require 'edit_in_place/builder'
|
6
5
|
require 'edit_in_place/configuration'
|
7
|
-
require 'edit_in_place/registrar'
|
8
6
|
require 'edit_in_place/extended_builder'
|
9
7
|
require 'edit_in_place/field_options'
|
10
8
|
require 'edit_in_place/field_type'
|
11
9
|
require 'edit_in_place/field_type_registrar'
|
10
|
+
require 'edit_in_place/middleware_registrar'
|
11
|
+
require 'edit_in_place/middleware_wrapper'
|
12
|
+
require 'edit_in_place/registrar'
|
13
|
+
require 'edit_in_place/version'
|
14
|
+
|
15
|
+
require 'edit_in_place/error'
|
16
|
+
require 'edit_in_place/duplicate_registration_error'
|
17
|
+
require 'edit_in_place/invalid_field_type_error'
|
18
|
+
require 'edit_in_place/invalid_registration_name_error'
|
19
|
+
require 'edit_in_place/unregistered_field_type_error'
|
20
|
+
require 'edit_in_place/unregistered_middleware_error'
|
21
|
+
require 'edit_in_place/unsupported_mode_error'
|
12
22
|
|
13
23
|
# {EditInPlace} is a namespace that contains all the modules and classes of the edit_in_place
|
14
24
|
# Rails gemified plugin.
|
@@ -16,22 +26,9 @@ require 'edit_in_place/field_type_registrar'
|
|
16
26
|
# @author Jacob Lockard
|
17
27
|
# @since 0.1.0
|
18
28
|
module EditInPlace
|
19
|
-
# {Error} is a subclass of {https://ruby-doc.org/core-2.5.0/StandardError.html StandardError}
|
20
|
-
# from which all custom errors in edit_in_place are derived. One potential use for this class
|
21
|
-
# is to rescue all custom errors produced by edit_in_place. For example:
|
22
|
-
#
|
23
|
-
# begin
|
24
|
-
# # Do something risky with edit_in_place here...
|
25
|
-
# rescue EditInPlace::Error
|
26
|
-
# # Catch any edit_in_place-specific error here...
|
27
|
-
# end
|
28
|
-
#
|
29
|
-
# @see https://ruby-doc.org/core-2.0.0/Exception.html
|
30
|
-
class Error < StandardError; end
|
31
|
-
|
32
29
|
@config = Configuration.new
|
33
30
|
|
34
|
-
# Gets the
|
31
|
+
# Gets the {Configuration} instance that contains the global configuration for the
|
35
32
|
# edit_in_place plugin. The global configuration will be applied to all created {Builder}
|
36
33
|
# instances.
|
37
34
|
# @return [Configuration] the global configuration.
|
@@ -40,7 +37,7 @@ module EditInPlace
|
|
40
37
|
@config
|
41
38
|
end
|
42
39
|
|
43
|
-
# Sets the
|
40
|
+
# Sets the {Configuration} instance that represents the global configuration for the
|
44
41
|
# edit_in_place plugin. A convenient use for this method is to reset the global configuration
|
45
42
|
# by setting it to +EditInPlace::Configuration.new+.
|
46
43
|
# @param config [Configuration] the global configuration.
|
@@ -65,8 +62,3 @@ module EditInPlace
|
|
65
62
|
yield config if block_given?
|
66
63
|
end
|
67
64
|
end
|
68
|
-
|
69
|
-
require 'edit_in_place/invalid_field_type_error'
|
70
|
-
require 'edit_in_place/unregistered_field_type_error'
|
71
|
-
require 'edit_in_place/invalid_registration_name_error'
|
72
|
-
require 'edit_in_place/duplicate_registration_error'
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'middlegem'
|
4
|
+
|
5
|
+
module EditInPlace
|
6
|
+
# {ArrayDefinition} is a subclass of +Middlegem::ArrayDefinition+ that works properly with
|
7
|
+
# {MiddlewareWrapper} instances. The problem is that +Middlegem::ArrayDefinition+, by default,
|
8
|
+
# tries to use the class of the defined middlewares themselves. By overriding {matches_class?},
|
9
|
+
# {ArrayDefinition} is able to cause +middlegem+ to use the class of the _base_ middleware of
|
10
|
+
# any {MiddlewareWrapper} instances.
|
11
|
+
#
|
12
|
+
# @author Jacob Lockard
|
13
|
+
# @since 0.2.0
|
14
|
+
class ArrayDefinition < Middlegem::ArrayDefinition
|
15
|
+
protected
|
16
|
+
|
17
|
+
# Overrides +matches_class?+ to use the base middleware's class if the middleware is a
|
18
|
+
# {MiddlewareWrapper}.
|
19
|
+
# @param middleware [Object] the middleware to check.
|
20
|
+
# @param klass [Class] the class against which the middleware should be checked.
|
21
|
+
# @return [Boolean] whether the given middleware has the given class.
|
22
|
+
def matches_class?(middleware, klass)
|
23
|
+
middleware.is_a?(MiddlewareWrapper) ? middleware.base.instance_of?(klass) : super
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -2,12 +2,13 @@
|
|
2
2
|
|
3
3
|
module EditInPlace
|
4
4
|
# {Builder} is the class that provides the actual functionality to build and render editable
|
5
|
-
# content.
|
6
|
-
# view. The view can then use its methods to generate content. Note
|
7
|
-
# created,
|
5
|
+
# content. If used in a Rails setting, this class will usually be instantiated in a controller
|
6
|
+
# and passed somehow to a view. The view can then use its methods to generate content. Note
|
7
|
+
# that when a {Builder} is created, its {Configuration} is copied from the global
|
8
|
+
# configuration.
|
8
9
|
#
|
9
10
|
# This class can be extended by utilizing {ExtendedBuilder} to safely add additional
|
10
|
-
# functionality. This can be particularly helpful for edit_in_place extensions that
|
11
|
+
# functionality. This can be particularly helpful for edit_in_place extensions that wish
|
11
12
|
# to add other content generation methods without requiring the user to yield another builder
|
12
13
|
# to the view.
|
13
14
|
#
|
@@ -25,15 +26,40 @@ module EditInPlace
|
|
25
26
|
@config = EditInPlace.config.dup
|
26
27
|
end
|
27
28
|
|
28
|
-
#
|
29
|
+
# Overrides +method_missing+ to allow methods like +*_field+ to be called for registered
|
30
|
+
# fields. For example, if a +:text+ field type has been registered, then calling
|
31
|
+
# +Builder#text_field(...)+ is equivalent to calling +Builder#field(:text, ...)+.
|
32
|
+
# @param method_name [Symbol, String] the name of the missing method being called.
|
33
|
+
# @param args [Array] the arguments passed to the missing method.
|
34
|
+
# @yield the block, if any, passed to the missing method.
|
35
|
+
# @return the result of calling {#field} with the appropriate type if possible; the result of
|
36
|
+
# calling +super+ if not.
|
37
|
+
# @since 0.2.0
|
38
|
+
def method_missing(method_name, *args, &block)
|
39
|
+
field_type = parse_field_method(method_name)
|
40
|
+
field_type ? field(field_type, *args, &block) : super
|
41
|
+
end
|
42
|
+
|
43
|
+
# Overrides +respond_to_missing?+ to allow methods like +*_field+ to be responded to by
|
44
|
+
# {Builder}.
|
45
|
+
# @param method_name [Symbol, String] the name of the missing method being checked.
|
46
|
+
# @return [Boolean] +true+ if this {Builder} can respond to the given method name; +false+
|
47
|
+
# otherwise.
|
48
|
+
# @see #method_missing
|
49
|
+
# @since 0.2.0
|
50
|
+
def respond_to_missing?(method_name, inlude_private = false)
|
51
|
+
parse_field_method(method_name) || super
|
52
|
+
end
|
53
|
+
|
54
|
+
# Creates a deep copy of this {Builder} whose configuration can be safely modified.
|
29
55
|
# @return [Builder] a deep copy of this {Builder}.
|
30
56
|
def dup
|
31
|
-
b =
|
57
|
+
b = self.class.new
|
32
58
|
b.config = config.dup
|
33
59
|
b
|
34
60
|
end
|
35
61
|
|
36
|
-
# Configures this {Builder} by yielding its configuration to the given block. For example
|
62
|
+
# Configures this {Builder} by yielding its configuration to the given block. For example:
|
37
63
|
#
|
38
64
|
# @builder = EditInPlace::Builder.new
|
39
65
|
# @builder.configure do |c|
|
@@ -41,71 +67,62 @@ module EditInPlace
|
|
41
67
|
# c.field_options.middlewares = [:one, :two]
|
42
68
|
# end
|
43
69
|
#
|
44
|
-
# Note that this method is simply a convenience method
|
70
|
+
# Note that this method is simply a convenience method and that the above code is exactly
|
45
71
|
# equivalent to the following:
|
46
72
|
#
|
47
73
|
# @builder = EditInPlace::Builder.new
|
48
74
|
# @builder.config.field_options.mode = :editing
|
49
75
|
# @builder.config.field_options.middlewares = [:one, :two]
|
76
|
+
#
|
50
77
|
# @yieldparam config [Configuration] the {Configuration} instance associated with this
|
51
78
|
# {Builder}.
|
52
79
|
# @yieldreturn [void]
|
53
80
|
# @return [void]
|
54
|
-
# @see EditInPlace.configure
|
55
81
|
def configure
|
56
82
|
yield config if block_given?
|
57
83
|
end
|
58
84
|
|
85
|
+
# Renders a single "field", that is, a single piece of editable content defined by a field
|
86
|
+
# type. Field options may or may not be provided and will be automatically injected if not.
|
87
|
+
# The input given to this method will be transformed by any middlewares added from various
|
88
|
+
# sources.
|
59
89
|
# @overload field(type, options, *args)
|
60
|
-
# Renders a single field of the given type with the given field
|
61
|
-
#
|
62
|
-
#
|
63
|
-
#
|
90
|
+
# Renders a single field of the given type with the given field options and input.
|
91
|
+
# @param type [FieldType, Class, Symbol] the type of field to render, either an actual
|
92
|
+
# instance of {FieldType}, a field type class that can be instantiated with no arguments,
|
93
|
+
# or the symbol name of a registered field type.
|
64
94
|
# @param options [FieldOptions, Hash] the field options to be used when rendering the
|
65
|
-
# field. These options are defined in {FieldOptions}. Either an actual instance of
|
95
|
+
# field. These options are as defined in {FieldOptions}. Either an actual instance of
|
66
96
|
# {FieldOptions} or a hash are acceptable.
|
67
|
-
# @param args [Array] the arguments to be passed to
|
68
|
-
# @return
|
97
|
+
# @param args [Array] the input arguments to be passed to `FieldType#render`.
|
98
|
+
# @return the rendered field.
|
69
99
|
# @overload field(type, *args)
|
70
|
-
# Renders a single field of the given type with the
|
71
|
-
#
|
72
|
-
#
|
73
|
-
#
|
74
|
-
#
|
75
|
-
# @param args [Array] the arguments to be passed to
|
76
|
-
# @return
|
100
|
+
# Renders a single field of the given type with the default field options and the given
|
101
|
+
# input.
|
102
|
+
# @param type [FieldType, Class, Symbol] the type of field to render, either an actual
|
103
|
+
# instance of {FieldType}, a field type class that can be instantiated with no arguments,
|
104
|
+
# or the symbol name of a registered field type.
|
105
|
+
# @param args [Array] the input arguments to be passed to `FieldType#render`.
|
106
|
+
# @return the rendered field.
|
77
107
|
def field(type, *args)
|
78
|
-
|
79
|
-
args
|
108
|
+
options = config.field_options.merge(get_field_options!(args))
|
109
|
+
args.unshift(options.mode)
|
80
110
|
|
81
|
-
|
82
|
-
stack = Middlegem::Stack.new(definition, middlewares: args[0].middlewares)
|
83
|
-
args = stack.call(*args)
|
111
|
+
args = apply_middlewares(options.middlewares, *args)
|
84
112
|
|
85
113
|
type = evaluate_field_type(type)
|
86
114
|
type.render(*args)
|
87
115
|
end
|
88
116
|
|
89
117
|
# Yields a new, scoped {Builder} with the given field options. This method is helpful when
|
90
|
-
# many fields require the same options.
|
91
|
-
# access to the current view context, like so:
|
92
|
-
#
|
93
|
-
# <!-- some_view.html.erb -->
|
94
|
-
#
|
95
|
-
# <%= @builder.scoped view: self do |b|
|
96
|
-
# <%= b.field(:example_type, 'random scoped field') %>
|
97
|
-
# <!-- ... -->
|
98
|
-
# <% end %>
|
99
|
-
#
|
100
|
-
# Now all fields generated using +b+ will have access to +self+ as the view context in
|
101
|
-
# {FieldOptions#view}.
|
118
|
+
# many fields require the same options.
|
102
119
|
# @yieldparam scoped_builder [Builder] the new, scoped {Builder} instance.
|
103
|
-
# @yieldreturn
|
120
|
+
# @yieldreturn the output.
|
104
121
|
# @param field_options [FieldOptions, Hash] the field options that the scoped builder should
|
105
122
|
# have. Note that these options will be merged (using {FieldOptions#merge!}) with the
|
106
123
|
# current ones. Either an actual {FieldOptions} instance or a hash of options are
|
107
124
|
# acceptable.
|
108
|
-
# @return
|
125
|
+
# @return the output of the block.
|
109
126
|
def scoped(field_options = {})
|
110
127
|
field_options = FieldOptions.new(field_options) unless field_options.is_a? FieldOptions
|
111
128
|
|
@@ -114,48 +131,104 @@ module EditInPlace
|
|
114
131
|
|
115
132
|
yield scoped_builder
|
116
133
|
end
|
134
|
+
# @since 0.2.0
|
135
|
+
alias scope scoped
|
136
|
+
|
137
|
+
# Yields a new, scoped {Builder} with the given middlewares merged into the current ones.
|
138
|
+
# Note that this method is for convenience only and is exactly equivalent to calling
|
139
|
+
# +scoped(middlewares: ...)+.
|
140
|
+
# @yieldparam scoped_builder [Builder] the new, scoped {Builder} instance.
|
141
|
+
# @yieldreturn the output.
|
142
|
+
# @param middlewares [Array] the array of middlewares that the scoped builder should have
|
143
|
+
# merged into it.
|
144
|
+
# @return the output of the block.
|
145
|
+
# @since 0.2.0
|
146
|
+
def with_middlewares(*middlewares, &block)
|
147
|
+
scoped(middlewares: middlewares, &block)
|
148
|
+
end
|
149
|
+
alias middleware_scope with_middlewares
|
117
150
|
|
118
151
|
private
|
119
152
|
|
120
|
-
#
|
121
|
-
#
|
122
|
-
#
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
# - Otherwise, the default {FieldOptions} instance is prepended to the argument list.
|
127
|
-
# @param args [Array] the raw arguments into which to inject the field options.
|
128
|
-
# @return [void]
|
129
|
-
def inject_field_options!(args)
|
130
|
-
options = args.first
|
153
|
+
# Attempts to get a field type from the given '*_field' method name.
|
154
|
+
# @param method_name [Symbol, String] the name of the method to parse.
|
155
|
+
# @return [FieldType, nil] the field type if one could be found; +nil+ if not.
|
156
|
+
def parse_field_method(method_name)
|
157
|
+
method_name = method_name.to_s
|
158
|
+
return nil unless method_name.end_with? '_field'
|
131
159
|
|
132
|
-
|
160
|
+
field_type_name = method_name.delete_suffix('_field').to_sym
|
161
|
+
config.field_types.find(field_type_name)
|
162
|
+
end
|
133
163
|
|
134
|
-
|
135
|
-
|
164
|
+
# Gets an appropriate {FieldOptions} instance for the given list of field arguments and
|
165
|
+
# removes any present field options from the argument list.
|
166
|
+
# @param args [Array] the raw arguments from which to get the field options.
|
167
|
+
# @return [FieldOptions] the retrieved field options.
|
168
|
+
# @since 0.2.0
|
169
|
+
def get_field_options!(args)
|
170
|
+
case args.first
|
171
|
+
when FieldOptions
|
172
|
+
args.shift
|
173
|
+
when Hash
|
174
|
+
FieldOptions.new(args.shift)
|
136
175
|
else
|
137
|
-
|
176
|
+
FieldOptions.new
|
138
177
|
end
|
139
178
|
end
|
140
179
|
|
141
180
|
# Gets an appropriate {FieldType} instance for the given raw field type argument. In
|
142
181
|
# particular:
|
143
|
-
# - When the input is already a FieldType instance, that instance is simply returned.
|
144
182
|
# - When the input is a symbol, attempts to find a registered field type associated with it.
|
183
|
+
# - When the input is a class, instatiates it.
|
184
|
+
# - When the input is already a FieldType instance, that instance is simply returned.
|
145
185
|
# - Otherwise, raises an error.
|
186
|
+
# @param type [Symbol, Class, FieldType] the raw field type argument to evaluate.
|
146
187
|
# @return [FieldType] an appropriate {FieldType} instance for the given input.
|
147
188
|
def evaluate_field_type(type)
|
148
189
|
case type
|
190
|
+
when Symbol
|
191
|
+
evaluate_field_type(lookup_field_type(type))
|
192
|
+
when Class
|
193
|
+
evaluate_field_type(type.new)
|
149
194
|
when FieldType
|
150
195
|
type
|
151
|
-
when Symbol
|
152
|
-
result = config.field_types.find(type)
|
153
|
-
raise UnregisteredFieldTypeError, type if result.nil?
|
154
|
-
|
155
|
-
result
|
156
196
|
else
|
157
197
|
raise InvalidFieldTypeError, type
|
158
198
|
end
|
159
199
|
end
|
200
|
+
|
201
|
+
# Attempts to find a field type registered with the given name in the field type registrar.
|
202
|
+
# If one could not be found, raises an appropriate error.
|
203
|
+
# @param name [Symbol] the name to search for.
|
204
|
+
# @return the found field type.
|
205
|
+
# @since 0.2.0
|
206
|
+
def lookup_field_type(name)
|
207
|
+
result = config.field_types.find(name)
|
208
|
+
raise UnregisteredFieldTypeError, name if result.nil?
|
209
|
+
|
210
|
+
result
|
211
|
+
end
|
212
|
+
|
213
|
+
# Applies the given array of middlewares to the given input arguments and returns the result.
|
214
|
+
# @param middlewares [Array] the array of middlewares to apply.
|
215
|
+
# @param args [Array] the array of input arguments.
|
216
|
+
# @return [Array] the resulting argument list.
|
217
|
+
# @since 0.2.0
|
218
|
+
def apply_middlewares(middlewares, *args)
|
219
|
+
definition = ArrayDefinition.new(config.defined_middlewares)
|
220
|
+
stack = Middlegem::Stack.new(definition, middlewares: wrap_middlewares(middlewares))
|
221
|
+
|
222
|
+
stack.call(*args)
|
223
|
+
end
|
224
|
+
|
225
|
+
# Converts each of the given middleware instances into a {MiddlewareWrapper} and returns the
|
226
|
+
# result.
|
227
|
+
# @param middlewares [Array] the middlewares to wrap.
|
228
|
+
# @return [Array] the wrapped middlewares.
|
229
|
+
# @since 0.2.0
|
230
|
+
def wrap_middlewares(middlewares)
|
231
|
+
middlewares.map { |m| MiddlewareWrapper.new(m, config.registered_middlewares) }
|
232
|
+
end
|
160
233
|
end
|
161
234
|
end
|
@@ -1,16 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'active_support/core_ext/object/deep_dup'
|
4
|
+
|
3
5
|
module EditInPlace
|
4
6
|
# {Configuration} is a class that is capable of storing configuration for an edit_in_place
|
5
|
-
# {Builder}. Essentially all the options provided by edit_in_place reside in this class for
|
7
|
+
# {Builder}. Essentially all the options provided by edit_in_place reside in this class, for
|
6
8
|
# easy reuse. This class is currently used in two locations---the global configuration in
|
7
9
|
# {EditInPlace.config} and the builder-specific configuration in {Builder#config}.
|
8
10
|
#
|
9
11
|
# @author Jacob Lockard
|
10
12
|
# @since 0.1.0
|
11
13
|
class Configuration
|
12
|
-
# The default mode in which fields should be rendered if left
|
13
|
-
#
|
14
|
+
# The default mode in which fields should be rendered if left unspecified in
|
15
|
+
# {#field_options}.
|
14
16
|
DEFAULT_MODE = :viewing
|
15
17
|
|
16
18
|
# The {FieldTypeRegistrar} used to store the list of registered field types.
|
@@ -27,21 +29,28 @@ module EditInPlace
|
|
27
29
|
# @return [Array] the array of defined middlewares.
|
28
30
|
attr_accessor :defined_middlewares
|
29
31
|
|
32
|
+
# The {MiddlewareRegistrar} used to store the list of registered middlewares.
|
33
|
+
# @return [MiddlewareRegistrar] the {MiddlewareRegistrar} that stores the list of registered
|
34
|
+
# middlewares.
|
35
|
+
attr_accessor :registered_middlewares
|
36
|
+
|
30
37
|
# Creates a new, default instance of {Configuration}.
|
31
38
|
def initialize
|
32
39
|
@field_types = FieldTypeRegistrar.new
|
33
40
|
@field_options = FieldOptions.new(mode: DEFAULT_MODE)
|
34
41
|
@defined_middlewares = []
|
42
|
+
@registered_middlewares = MiddlewareRegistrar.new
|
35
43
|
end
|
36
44
|
|
37
45
|
# Creates a deep copy of this {Configuration} that can be safely modified.
|
38
46
|
# @return [Configuration] a deep copy of this configuration.
|
39
47
|
def dup
|
40
|
-
c =
|
48
|
+
c = self.class.new
|
41
49
|
c.field_types = field_types.dup
|
42
50
|
c.field_options = field_options.dup
|
43
51
|
# Note that this is purposely NOT a deep copy---it doesn't make sense to duplicate classes.
|
44
52
|
c.defined_middlewares = defined_middlewares.dup
|
53
|
+
c.registered_middlewares = registered_middlewares.dup
|
45
54
|
c
|
46
55
|
end
|
47
56
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module EditInPlace
|
4
|
+
# {Error} is a subclass of {https://ruby-doc.org/core-2.5.0/StandardError.html StandardError}
|
5
|
+
# from which all custom errors in edit_in_place are derived. One potential use for this class
|
6
|
+
# is to rescue all custom errors produced by edit_in_place. For example:
|
7
|
+
#
|
8
|
+
# begin
|
9
|
+
# # Do something risky with edit_in_place here...
|
10
|
+
# rescue EditInPlace::Error
|
11
|
+
# # Catch any edit_in_place-specific error here...
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# @see https://ruby-doc.org/core-2.0.0/Exception.html
|
15
|
+
class Error < StandardError; end
|
16
|
+
end
|
@@ -29,12 +29,33 @@ module EditInPlace
|
|
29
29
|
# quack like a {Builder}.
|
30
30
|
attr_reader :base
|
31
31
|
|
32
|
-
delegate_missing_to :base
|
33
|
-
|
34
32
|
# Creates a new {ExtendedBuilder} with the given base builder.
|
35
33
|
# @param base [Object] the base builder to extend. It should quack like a {Builder}.
|
36
34
|
def initialize(base)
|
37
35
|
@base = base
|
38
36
|
end
|
37
|
+
|
38
|
+
# Overrides +method_missing+ to allow the use of methods defined on the base builder. This
|
39
|
+
# method was added in version 0.2.0 when Rails support (and thus support for
|
40
|
+
# +delegate_missing_to+) was removed.
|
41
|
+
# @param method_name [Symbol, String] the name of the missing method.
|
42
|
+
# @param args [Array] the arguments given to the missing method.
|
43
|
+
# @yield the block passed to the missing method.
|
44
|
+
# @return the result of calling the method on the base builder, if it was defined, or the
|
45
|
+
# result of calling +super+ if not.
|
46
|
+
# @since 0.2.0
|
47
|
+
def method_missing(method_name, *args, &block)
|
48
|
+
base.respond_to?(method_name) ? base.send(method_name, *args, &block) : super
|
49
|
+
end
|
50
|
+
|
51
|
+
# Overrides +respond_to_missing?+ to respond to methods defined on the base builder. This
|
52
|
+
# method was added in version 0.2.0 when Rails support (and thus support for
|
53
|
+
# +delegate_missing_to+) was removed.
|
54
|
+
# @param method_name [Symbol, String] the name of the missing method.
|
55
|
+
# @return [Boolean] whether this {ExtendedBuilder} can respond to the given method name.
|
56
|
+
# @since 0.2.0
|
57
|
+
def respond_to_missing?(method_name, priv = false)
|
58
|
+
base.respond_to?(method_name) || super
|
59
|
+
end
|
39
60
|
end
|
40
61
|
end
|
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
module EditInPlace
|
4
4
|
# {FieldOptions} is a class that stores the options and context required to render a field.
|
5
|
+
# More specifically, it stores options given to *the {Builder#field} method itself*, not
|
6
|
+
# options given to the field renderer.
|
5
7
|
#
|
6
8
|
# @author Jacob Lockard
|
7
9
|
# @since 0.1.0
|
@@ -18,11 +20,6 @@ module EditInPlace
|
|
18
20
|
# @return [void]
|
19
21
|
attr_reader :mode
|
20
22
|
|
21
|
-
# The view context to be used when rendering the field. This view context can be derived in
|
22
|
-
# a number of ways, most commonly by creating a new instance of +ActionView::Base+.
|
23
|
-
# @return the view context to be used when rendering the field.
|
24
|
-
attr_accessor :view
|
25
|
-
|
26
23
|
# An array of middleware instances that should be applied to the field's input.
|
27
24
|
# @return the array of middlewares.
|
28
25
|
attr_accessor :middlewares
|
@@ -31,12 +28,11 @@ module EditInPlace
|
|
31
28
|
# Creates a new instance of {FieldOptions} with the given options.
|
32
29
|
# @param options [Hash, #[]] a hash containing the given field options.
|
33
30
|
# @option options [Symbol] :mode the {#mode} in which fields should be rendered.
|
34
|
-
# @option options :
|
31
|
+
# @option options [Array] :middlewares the {#middlewares} for the field.
|
35
32
|
# @overload initialize
|
36
33
|
# Creates a new instance of {FieldOptions} with the default options.
|
37
34
|
def initialize(options = {})
|
38
35
|
self.mode = options[:mode]
|
39
|
-
self.view = options[:view]
|
40
36
|
self.middlewares = options[:middlewares] || []
|
41
37
|
end
|
42
38
|
|
@@ -48,24 +44,22 @@ module EditInPlace
|
|
48
44
|
# Creates a deep copy of this {FieldOptions} instance that can be safely modified.
|
49
45
|
# @return [FieldOptions] a deep copy of this {FieldOptions} instance.
|
50
46
|
def dup
|
51
|
-
f =
|
47
|
+
f = self.class.new
|
52
48
|
f.mode = mode
|
53
|
-
f.
|
54
|
-
f.middlewares = middlewares.deep_dup
|
49
|
+
f.middlewares = middlewares.map { |m| m.instance_of?(Class) ? m : m.dup }
|
55
50
|
f
|
56
51
|
end
|
57
52
|
|
58
|
-
# Merges the given {FieldOptions} instance into this one.
|
59
|
-
# other instance will overwrite
|
60
|
-
# duplicated before being merged, so it can be
|
61
|
-
#
|
53
|
+
# Merges the given {FieldOptions} instance into this one. A mode from the
|
54
|
+
# other instance will overwrite the one in this instance if present. Middleware arrays will
|
55
|
+
# be merged. Note that the other instance is duplicated before being merged, so it can be
|
56
|
+
# safely modified after the fact.
|
62
57
|
# @param other [FieldOptions] the other field options to merge into this one.
|
63
58
|
# @return [void]
|
64
59
|
def merge!(other)
|
65
60
|
other = other.dup
|
66
61
|
|
67
62
|
self.mode = other.mode unless other.mode.nil?
|
68
|
-
self.view = other.view unless other.view.nil?
|
69
63
|
self.middlewares += other.middlewares
|
70
64
|
end
|
71
65
|
|
@@ -3,13 +3,13 @@
|
|
3
3
|
module EditInPlace
|
4
4
|
# {FieldType} is a class that represents a single type of field. A field is a single,
|
5
5
|
# self-contained component that displays data in various "modes", typically either "viewing" or
|
6
|
-
# "editing". Field types provide the templates for
|
6
|
+
# "editing". Field types provide the templates for rendering fields.
|
7
7
|
#
|
8
8
|
# @abstract
|
9
9
|
# @author Jacob Lockard
|
10
10
|
# @since 0.1.0
|
11
11
|
class FieldType
|
12
|
-
# Render the field, given
|
12
|
+
# Render the field, given the mode and an array of arguments passed by
|
13
13
|
# the caller.
|
14
14
|
#
|
15
15
|
# While subclasses may override this method as appropriate, the default
|
@@ -18,13 +18,12 @@ module EditInPlace
|
|
18
18
|
# 2. calls a +render_*+ method.
|
19
19
|
# For example, if the mode were +:admin_editing+, then a +render_admin_editing+ method would
|
20
20
|
# be called. Naturally, the +render_*+ methods need to be defined by the subclass.
|
21
|
-
# @param
|
22
|
-
# be used to render the field.
|
21
|
+
# @param mode [Symbol] the mode with which to render the field.
|
23
22
|
# @param args [Array<Object>] the arguments passed by the field creator.
|
24
|
-
# @return
|
25
|
-
def render(
|
26
|
-
validate_mode!(
|
27
|
-
send("render_#{
|
23
|
+
# @return the rendered result.
|
24
|
+
def render(mode, *args)
|
25
|
+
validate_mode!(mode)
|
26
|
+
send("render_#{mode}", mode, *args)
|
28
27
|
end
|
29
28
|
|
30
29
|
# Gets the modes that are supported by this field type, +:viewing+ and +:editing+ by default.
|
@@ -1,22 +1,26 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'edit_in_place/registrar'
|
4
|
+
|
3
5
|
module EditInPlace
|
4
6
|
# {FieldTypeRegistrar} is a subcalss of {Registrar} that only allows {FieldType}
|
5
|
-
# instances to be registered.
|
7
|
+
# instances and field type classes to be registered.
|
6
8
|
#
|
7
9
|
# @author Jacob Lockard
|
8
10
|
# @since 0.1.0
|
9
11
|
class FieldTypeRegistrar < Registrar
|
10
12
|
protected
|
11
13
|
|
12
|
-
# Adds to the default
|
13
|
-
# {FieldType} instances can be registered.
|
14
|
+
# Adds to the default +validate_registration!+ implementation by ensuring that only
|
15
|
+
# {FieldType} instances or field type classes can be registered.
|
14
16
|
# @param name [Symbol] the name to validate.
|
15
|
-
# @param field_type [FieldType] the field type to validate.
|
17
|
+
# @param field_type [FieldType, Class] the field type to validate.
|
16
18
|
# @return [void]
|
17
19
|
def validate_registration!(name, field_type)
|
18
20
|
super
|
19
|
-
|
21
|
+
unless field_type.is_a?(FieldType) || field_type.instance_of?(Class)
|
22
|
+
raise InvalidFieldTypeError, field_type
|
23
|
+
end
|
20
24
|
end
|
21
25
|
end
|
22
26
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module EditInPlace
|
4
|
+
# {MiddlewareRegistrar} is a subclass of {Registrar} that only allows middleware objects (as
|
5
|
+
# defined by +Middlegem::Middleware.valid?+) or middleware classes to be registered.
|
6
|
+
#
|
7
|
+
# @author Jacob Lockard
|
8
|
+
# @since 0.2.0
|
9
|
+
class MiddlewareRegistrar < Registrar
|
10
|
+
protected
|
11
|
+
|
12
|
+
# Adds to the default +validate_registration!+ implementation by ensuring that only
|
13
|
+
# middleware objects (as defined by +Middlegem::Middleware.valid?+) or middleware classes
|
14
|
+
# can be registered.
|
15
|
+
def validate_registration!(name, middleware)
|
16
|
+
super
|
17
|
+
unless Middlegem::Middleware.valid?(middleware) || middleware.instance_of?(Class)
|
18
|
+
raise Middlegem::InvalidMiddlewareError
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'middlegem'
|
4
|
+
|
5
|
+
module EditInPlace
|
6
|
+
# {MiddlewareWrapper} is a class that provides a consistent wrapper for a middleware, which
|
7
|
+
# currently can be one of the following:
|
8
|
+
# 1. an actual middleware object (i.e. an object that responds to +#call+),
|
9
|
+
# 2. a symbol name corresponding to a registered middleware, or
|
10
|
+
# 3. a parameterless middleware class that should be instantiated.
|
11
|
+
# {MiddlewareWrapper}, which is itself a +Middlegem::Middleware+ does all the necessary
|
12
|
+
# conversions, providing a simple {#call} method.
|
13
|
+
#
|
14
|
+
# @author Jacob Lockard
|
15
|
+
# @since 0.2.0
|
16
|
+
class MiddlewareWrapper < Middlegem::Middleware
|
17
|
+
# The base middleware, which should be a middleware object, a middleware class, or a
|
18
|
+
# middleware name.
|
19
|
+
# @return the base middleware.
|
20
|
+
attr_reader :base
|
21
|
+
|
22
|
+
# Creates a new instance of {MiddlewareWrapper} with the given middleware and registrar.
|
23
|
+
# @param base the middleware to wrap.
|
24
|
+
# @param registrar [MiddlewareRegistrar] the middleware registrar in which to search for
|
25
|
+
# registered middlewares.
|
26
|
+
def initialize(base, registrar)
|
27
|
+
base = lookup_middleware(base, registrar) if base.is_a? Symbol
|
28
|
+
base = base.new if base.instance_of? Class
|
29
|
+
|
30
|
+
@base = base
|
31
|
+
|
32
|
+
super()
|
33
|
+
end
|
34
|
+
|
35
|
+
# Executes this {MiddlewareWrapper} with the given input by delegating appropriately to the
|
36
|
+
# base middleware.
|
37
|
+
# @param args [Array] the input arguments.
|
38
|
+
# @return [Array] the transformed output.
|
39
|
+
def call(*args)
|
40
|
+
base.call(*args)
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
# Attempts to find a middleware registered with the given name in the middleware registrar.
|
46
|
+
# If one could not be found, raises an appropriate error.
|
47
|
+
# @param name [Symbol] the name to search for.
|
48
|
+
# @return the found middleware.
|
49
|
+
def lookup_middleware(name, registrar)
|
50
|
+
result = registrar.find(name)
|
51
|
+
raise UnregisteredMiddlewareError, name if result.nil?
|
52
|
+
|
53
|
+
result
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module EditInPlace
|
4
4
|
# {Registrar} is a class that is capable of storing a list of objects registered with symbol
|
5
5
|
# names. Note that it makes no attempt to validate the objects registered. If such validation
|
6
|
-
# is required feel free to subclass {Registrar} and override the {validate_registration!}
|
6
|
+
# is required feel free to subclass {Registrar} and override the {#validate_registration!}
|
7
7
|
# method.
|
8
8
|
#
|
9
9
|
# @author Jacob Lockard
|
@@ -17,7 +17,7 @@ module EditInPlace
|
|
17
17
|
# Creates a deep copy of this {Registrar} that can be safely modified.
|
18
18
|
# @return [Registrar] a deep copy of this registrar.
|
19
19
|
def dup
|
20
|
-
r =
|
20
|
+
r = self.class.new
|
21
21
|
r.register_all(all)
|
22
22
|
r
|
23
23
|
end
|
@@ -59,17 +59,15 @@ module EditInPlace
|
|
59
59
|
# internal one and can be safely modified.
|
60
60
|
# @return [Hash<(Symbol, Object)>] the hash of registered names and objects.
|
61
61
|
def all
|
62
|
-
|
62
|
+
# Be sure to not duplicate classes.
|
63
|
+
registrations.transform_values { |r| r.instance_of?(Class) ? r : r.deep_dup }
|
63
64
|
end
|
64
65
|
|
65
66
|
protected
|
66
67
|
|
67
|
-
# @return [Hash<(Symbol, Object)>] A hash of registrations.
|
68
|
-
attr_reader :registrations
|
69
|
-
|
70
68
|
# Should ensure that the given registration is valid. By default a registration is valid if
|
71
|
-
# its name is not already taken and is a symbol. Subclasses may override this method to
|
72
|
-
# validation rules. Errors should be raised for any invalid registrations.
|
69
|
+
# its name is not already taken and it is a symbol. Subclasses may override this method to
|
70
|
+
# add validation rules. Errors should be raised for any invalid registrations.
|
73
71
|
# @param name [Symbol] the name to validate.
|
74
72
|
# @param _object [Object] the object to validate.
|
75
73
|
# @return [void]
|
@@ -86,5 +84,11 @@ module EditInPlace
|
|
86
84
|
ERR
|
87
85
|
end
|
88
86
|
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
# The hash of registrations contained in this {Registrar}.
|
91
|
+
# @return [Hash<(Symbol, Object)>] the hash of registrations.
|
92
|
+
attr_reader :registrations
|
89
93
|
end
|
90
94
|
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module EditInPlace
|
4
|
+
# An error that occurs when no registered middlewares exist for the middleare name given.
|
5
|
+
class UnregisteredMiddlewareError < Error
|
6
|
+
# Creates a new instance of {UnregisteredMiddlewareError} with the given middleware name that
|
7
|
+
# caused the error.
|
8
|
+
# @param name [Symbol] the middleware that caused the error; used in the error message.
|
9
|
+
def initialize(name)
|
10
|
+
super("No middlewares are registered with the name '#{name}'")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: edit_in_place
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jacob
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-07-
|
11
|
+
date: 2021-07-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: byebug
|
@@ -25,19 +25,19 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '11.0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name: rspec
|
28
|
+
name: rspec
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '3.10'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '3.10'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rubocop
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -81,42 +81,35 @@ dependencies:
|
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0.17'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
84
|
+
name: activesupport
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- -
|
87
|
+
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version:
|
89
|
+
version: 6.1.3
|
90
90
|
type: :runtime
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
|
-
- -
|
94
|
+
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version:
|
96
|
+
version: 6.1.3
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
98
|
+
name: middlegem
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
|
-
- -
|
102
|
-
- !ruby/object:Gem::Version
|
103
|
-
version: 6.1.3
|
104
|
-
- - ">="
|
101
|
+
- - '='
|
105
102
|
- !ruby/object:Gem::Version
|
106
|
-
version:
|
103
|
+
version: 0.2.0
|
107
104
|
type: :runtime
|
108
105
|
prerelease: false
|
109
106
|
version_requirements: !ruby/object:Gem::Requirement
|
110
107
|
requirements:
|
111
|
-
- -
|
112
|
-
- !ruby/object:Gem::Version
|
113
|
-
version: 6.1.3
|
114
|
-
- - ">="
|
108
|
+
- - '='
|
115
109
|
- !ruby/object:Gem::Version
|
116
|
-
version:
|
117
|
-
description: 'edit_in_place is a
|
118
|
-
|
119
|
-
place" way.
|
110
|
+
version: 0.2.0
|
111
|
+
description: 'edit_in_place is a gem that allows the creation of user interfaces that
|
112
|
+
allow the user to edit content in an intuitive, natural, "in place" way.
|
120
113
|
|
121
114
|
'
|
122
115
|
email:
|
@@ -129,18 +122,22 @@ files:
|
|
129
122
|
- README.md
|
130
123
|
- Rakefile
|
131
124
|
- lib/edit_in_place.rb
|
125
|
+
- lib/edit_in_place/array_definition.rb
|
132
126
|
- lib/edit_in_place/builder.rb
|
133
127
|
- lib/edit_in_place/configuration.rb
|
134
128
|
- lib/edit_in_place/duplicate_registration_error.rb
|
129
|
+
- lib/edit_in_place/error.rb
|
135
130
|
- lib/edit_in_place/extended_builder.rb
|
136
131
|
- lib/edit_in_place/field_options.rb
|
137
132
|
- lib/edit_in_place/field_type.rb
|
138
133
|
- lib/edit_in_place/field_type_registrar.rb
|
139
134
|
- lib/edit_in_place/invalid_field_type_error.rb
|
140
135
|
- lib/edit_in_place/invalid_registration_name_error.rb
|
141
|
-
- lib/edit_in_place/
|
136
|
+
- lib/edit_in_place/middleware_registrar.rb
|
137
|
+
- lib/edit_in_place/middleware_wrapper.rb
|
142
138
|
- lib/edit_in_place/registrar.rb
|
143
139
|
- lib/edit_in_place/unregistered_field_type_error.rb
|
140
|
+
- lib/edit_in_place/unregistered_middleware_error.rb
|
144
141
|
- lib/edit_in_place/unsupported_mode_error.rb
|
145
142
|
- lib/edit_in_place/version.rb
|
146
143
|
- lib/tasks/edit_in_place_tasks.rake
|
@@ -166,5 +163,5 @@ requirements: []
|
|
166
163
|
rubygems_version: 3.2.3
|
167
164
|
signing_key:
|
168
165
|
specification_version: 4
|
169
|
-
summary: A
|
166
|
+
summary: A gem that facilitates the creation of intuitive editable content.
|
170
167
|
test_files: []
|