formify 0.15.0 → 0.16.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
  SHA256:
3
- metadata.gz: 1bd463cb45e56a78846363c509874052ea0b0e172c8dfd4b7015f652dce998c7
4
- data.tar.gz: ead9810fb43a0bdad43f919221ed2c964aef6e26fa032898b029ae4998b1e6aa
3
+ metadata.gz: '09b226440478b6b89edbee6d137934281da2c347fd48681e5b205eb4039e0582'
4
+ data.tar.gz: 85399692540ed00fe2f26f88273f300a9a7bdaa1a7c82c95102f3a59b89d8aa2
5
5
  SHA512:
6
- metadata.gz: c8f31a0487e61fcc35ece263d92f90737b0e63e7defb194730a80fb1005695438378cf5b8c8b649715a819a94aa36b81c8fd31b5a552959c456cfc81f220505a
7
- data.tar.gz: 7df77eb9274c738e10cc9301cc798a3a815656d18aee60887ea2feb538682f49227608e9e8a917db9e96e975fe7e87325d684f17aba705bd980a820539d90cf6
6
+ metadata.gz: 6646b9167f6ab241864284b64e88b3ec851e4fa7a4c00f08597e53e32bfbdc1a50a084ef65152bfa1ed9796c51f74f276652da618195f4189797fe92784de282
7
+ data.tar.gz: 3af2ea3e9492d8cd839b2198bb96153fa18b71ce4890e40407cfea4b1a07b3741d3a8d0130e4c94599db6868e502f433adc9350f93fdf4d9f95c0b626f28a626
data/Gemfile.lock CHANGED
@@ -1,9 +1,10 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- formify (0.15.0)
4
+ formify (0.16.0)
5
5
  rails
6
6
  resonad
7
+ with_advisory_lock
7
8
 
8
9
  GEM
9
10
  remote: https://rubygems.org/
@@ -130,6 +131,8 @@ GEM
130
131
  websocket-driver (0.7.0)
131
132
  websocket-extensions (>= 0.1.0)
132
133
  websocket-extensions (0.1.3)
134
+ with_advisory_lock (4.0.0)
135
+ activerecord (>= 4.2)
133
136
 
134
137
  PLATFORMS
135
138
  ruby
data/README.md CHANGED
@@ -20,7 +20,181 @@ Or install it yourself as:
20
20
 
21
21
  ## Usage
22
22
 
23
- Once installed, use `include Formify::Form` into any class to give it the form structure.
23
+ Once installed, use `include Formify::Form` into any class to give it the form structure. In rspec tests, you can `require 'formify/spec_helpers` and then `include Formify::SpecHelpers`. This gives you access to [a number of helper methods](lib/formify/spec_helpers.rb).
24
+
25
+ ## Generators
26
+
27
+ Formify ships with a powerful generator, giving you some default scaffolding. It's designed to generate everything for you, letting you know what's optional and can be removed. You can generate forms and their respective spec files using the following:
28
+
29
+ `rails generate form ATTRIBUTES`
30
+
31
+ `ATTRIBUTES` is a list of form attributes and their delegates. Formify takes advantage of existing objects (e.g. models and other forms) and their attribute reading and writing functionality. For example, if you were to do the following:
32
+
33
+ `rails generate form widgets/create foo:bar,baz owner:owner_attribute created_by`
34
+
35
+ You would get the following files:
36
+
37
+ - `app/lib/forms/widgets/create.rb`
38
+ - `spec/lib/forms/widgets/create_spec.rb`
39
+
40
+ ### Generated Form
41
+
42
+ ```ruby
43
+ # frozen_string_literal: true
44
+
45
+ module Forms
46
+ module Widgets
47
+ class Create
48
+ include Formify::Form
49
+
50
+ attr_accessor :created_by,
51
+ :foo,
52
+ :owner
53
+
54
+ delegate_accessor :bar,
55
+ :baz,
56
+ to: :foo
57
+ delegate_accessor :owner_attribute,
58
+ to: :owner
59
+
60
+ validates_presence_of :bar,
61
+ :baz,
62
+ :created_by,
63
+ :foo,
64
+ :owner,
65
+ :owner_attribute
66
+
67
+ # validate :validate_something
68
+
69
+ initialize_with :created_by, :foo, :owner do |attributes|
70
+ puts attributes
71
+ end
72
+
73
+ def save
74
+ raise NotImplementedError
75
+
76
+ with_advisory_lock_transaction(:foo) do
77
+ validate_or_fail
78
+ .and_then { create_widget }
79
+ .and_then { success(created_by) }
80
+ end
81
+ end
82
+
83
+ private
84
+
85
+ def create_widget
86
+ end
87
+
88
+ # def validate_something
89
+ # end
90
+ end
91
+ end
92
+ end
93
+ ```
94
+
95
+ ### Generated Spec
96
+
97
+ ```ruby
98
+ # frozen_string_literal: true
99
+
100
+ require 'rails_helper'
101
+ require 'formify/spec_helpers'
102
+
103
+ describe Forms::Widgets::Create, type: :form do
104
+ include Formify::SpecHelpers
105
+
106
+ # :attributes is used to initialize the form.
107
+ # These values should result in a valid form.
108
+ # You can override these in blocks or use let(:attributes_override) { { foo: bar } }
109
+ let(:attributes) do
110
+ {
111
+ bar: BAR_VALUE,
112
+ baz: BAZ_VALUE,
113
+ created_by: CREATED_BY_VALUE,
114
+ foo: FOO_VALUE,
115
+ owner: OWNER_VALUE,
116
+ owner_attribute: OWNER_ATTRIBUTE_VALUE
117
+ }
118
+ end
119
+
120
+ it { expect_valid } # Expect the form to be valid
121
+ it { expect(result).to be_success }
122
+ it { expect(value).to be_a(Widget) } # Model name inferred
123
+
124
+ context '#bar' do
125
+ # Attribute: :bar
126
+ it { expect_error_with_missing_attribute(:bar) }
127
+ xit { expect_error_with_attribute_value(:bar, BAR_BAD_VALUE, message: nil) } # :message is optional
128
+ xit { expect_valid_with_attribute_value(:bar, BAR_GOOD_VALUE) }
129
+ end
130
+
131
+ context '#baz' do
132
+ # Attribute: :baz
133
+ it { expect_error_with_missing_attribute(:baz) }
134
+ xit { expect_error_with_attribute_value(:baz, BAZ_BAD_VALUE, message: nil) } # :message is optional
135
+ xit { expect_valid_with_attribute_value(:baz, BAZ_GOOD_VALUE) }
136
+ end
137
+
138
+ context '#created_by' do
139
+ # Attribute: :created_by
140
+ it { expect_error_with_missing_attribute(:created_by) }
141
+ xit { expect_error_with_attribute_value(:created_by, CREATED_BY_BAD_VALUE, message: nil) } # :message is optional
142
+ xit { expect_valid_with_attribute_value(:created_by, CREATED_BY_GOOD_VALUE) }
143
+ end
144
+
145
+ context '#foo' do
146
+ # Attribute: :foo
147
+ it { expect_error_with_missing_attribute(:foo) }
148
+ xit { expect_error_with_attribute_value(:foo, FOO_BAD_VALUE, message: nil) } # :message is optional
149
+ xit { expect_valid_with_attribute_value(:foo, FOO_GOOD_VALUE) }
150
+ end
151
+
152
+ context '#owner' do
153
+ # Attribute: :owner
154
+ it { expect_error_with_missing_attribute(:owner) }
155
+ xit { expect_error_with_attribute_value(:owner, OWNER_BAD_VALUE, message: nil) } # :message is optional
156
+ xit { expect_valid_with_attribute_value(:owner, OWNER_GOOD_VALUE) }
157
+ end
158
+
159
+ context '#owner_attribute' do
160
+ # Attribute: :owner_attribute
161
+ it { expect_error_with_missing_attribute(:owner_attribute) }
162
+ xit { expect_error_with_attribute_value(:owner_attribute, OWNER_ATTRIBUTE_BAD_VALUE, message: nil) } # :message is optional
163
+ xit { expect_valid_with_attribute_value(:owner_attribute, OWNER_ATTRIBUTE_GOOD_VALUE) }
164
+ end
165
+
166
+ # Other Expectation Helpers
167
+ # xit { expect_error_message(message) }
168
+ # xit { expect_error_with_attribute(attribute) }
169
+ # xit { expect_not_valid(attribute: nil, message: nil) } # :attribute and :message are optional
170
+ end
171
+
172
+ ```
173
+
174
+ ### Options
175
+
176
+ - `pluralize_collection` (default: `true`) - Pluralize the collection as per naming conventions below.
177
+
178
+ ## Transactions
179
+
180
+ Formify works with ClosureTree/with_advisory_lock to offer easy and intuitive locking, with or without transactions.
181
+
182
+ ## Naming Conventions
183
+
184
+ ### Form Name
185
+ Formify assumes that every form is an action. As such, it's best to use verbs like `create`, `update`, `destroy`, `upsert`, `find`, `process`, etc.
186
+
187
+ ### Form Collection
188
+ The parent folder of a form should be the plural of the object it operates on. For example, if you had a `User` model, you would have `users/create.rb`. The collection does not need to be a model. It can be any noun in the plural form, like `sessions/create.rb`
189
+
190
+ ### Collection Scopes
191
+
192
+ Any folder before the collection is considered a scope and serves to help you group collections and forms. Consider an application that has a end-user and an admin, and accounts need to be approved. You would probably consider the following actions to need separate functionality:
193
+
194
+ - `/forms/admin/accounts/create.rb` - This one may take an `approved_by` attribute.
195
+ - `/forms/accounts/create.rb` - This one would result in account pending approval
196
+
197
+ Scoping helps you keep your
24
198
 
25
199
  ## Development
26
200
 
data/formify.gemspec CHANGED
@@ -27,4 +27,5 @@ Gem::Specification.new do |spec|
27
27
  spec.add_development_dependency 'rspec', '~> 3.0'
28
28
  spec.add_runtime_dependency 'rails'
29
29
  spec.add_runtime_dependency 'resonad'
30
+ spec.add_runtime_dependency 'with_advisory_lock'
30
31
  end
@@ -1,3 +1,3 @@
1
1
  module Formify
2
- VERSION = '0.15.0'.freeze
2
+ VERSION = '0.16.0'.freeze
3
3
  end
@@ -11,6 +11,20 @@ class FormGenerator < Rails::Generators::NamedBase
11
11
  required: true,
12
12
  description: 'A list of attributes and their delegates: foo:bar,baz'
13
13
 
14
+ class_option :pluralize_collection,
15
+ default: true,
16
+ description: 'Disable naming convention transformations like forcing plural collections',
17
+ type: :boolean
18
+
19
+ def transform_naming
20
+ return unless pluralize_collection?
21
+
22
+ self.name = begin
23
+ names = name.split('/')
24
+ names[0..-3].append(names[-2].pluralize).append(names[-1]).join('/')
25
+ end
26
+ end
27
+
14
28
  def validate_fixed_attrs
15
29
  return unless duplicate_attributes.any?
16
30
 
@@ -18,11 +32,11 @@ class FormGenerator < Rails::Generators::NamedBase
18
32
  end
19
33
 
20
34
  def generate_form
21
- template 'form.rb', File.join('app/lib/forms', class_path, "#{file_name}.rb")
35
+ template 'form.rb', File.join('app/lib/forms', transformed_class_path, "#{file_name}.rb")
22
36
  end
23
37
 
24
38
  def generate_form_spec
25
- template 'form_spec.rb', File.join('spec/lib/forms', class_path, "#{file_name}_spec.rb")
39
+ template 'form_spec.rb', File.join('spec/lib/forms', transformed_class_path, "#{file_name}_spec.rb")
26
40
  end
27
41
 
28
42
  private
@@ -47,6 +61,14 @@ class FormGenerator < Rails::Generators::NamedBase
47
61
  ]
48
62
  end
49
63
 
64
+ def collection
65
+ @collection ||= split_name[-2].pluralize.underscore
66
+ end
67
+
68
+ def collection_name
69
+ @collection_name ||= collection.camelcase
70
+ end
71
+
50
72
  def delegated_attributes
51
73
  @delegated_attributes ||= attributes_and_delegates
52
74
  .select { |_k, v| v.sort!.present? }
@@ -59,8 +81,16 @@ class FormGenerator < Rails::Generators::NamedBase
59
81
  .map(&:first)
60
82
  end
61
83
 
84
+ def form
85
+ @form ||= name.split('/').last.to_s.underscore
86
+ end
87
+
88
+ def form_name
89
+ @form_name ||= form.camelcase
90
+ end
91
+
62
92
  def inferred_model_name
63
- @inferred_model_name ||= name.split('/')[-2].singularize.camelcase
93
+ @inferred_model_name ||= collection.singularize.camelcase
64
94
  end
65
95
 
66
96
  def module_namespacing(&block)
@@ -72,7 +102,33 @@ class FormGenerator < Rails::Generators::NamedBase
72
102
  end
73
103
 
74
104
  def modules
75
- @modules ||= ['Forms'] + name.split('/')[0..-2].map(&:to_s).map(&:camelcase)
105
+ @modules ||= ['Forms'] + split_name[0..-2].map(&:to_s).map(&:camelcase)
106
+ end
107
+
108
+ def pluralize_collection?
109
+ @pluralize_collection ||= options[:pluralize_collection]
110
+ end
111
+
112
+ def scopes
113
+ @scopes ||= split_name[0..-3].map(&:underscore)
114
+ end
115
+
116
+ def scope_names
117
+ @scope_names ||= scopes.map(&:camelcase)
118
+ end
119
+
120
+ def split_name
121
+ @split_name ||= name.split('/')
122
+ end
123
+
124
+ def transformed_class_path
125
+ @transformed_class_path ||= begin
126
+ if pluralize_collection?
127
+ class_path[0..-2].append(class_path[-1].pluralize)
128
+ else
129
+ class_path
130
+ end
131
+ end
76
132
  end
77
133
 
78
134
  def variablefy(val)
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  <% module_namespacing do -%>
4
- class <%= name.split('/').last.to_s.camelcase %>
4
+ class <%= form_name %>
5
5
  include Formify::Form
6
6
 
7
7
  attr_accessor :<%= attributes.first %><%= ',' if attributes.count > 1 %>
@@ -21,17 +21,15 @@ describe Forms::<%= class_name %>, type: :form do
21
21
  it { expect(result).to be_success }
22
22
  it { expect(value).to be_a(<%= inferred_model_name %>) } # Model name inferred
23
23
 
24
- ### START: Attribute Expectations
25
-
26
24
  <% all_attributes.each do |attr| -%>
27
- # Attribute: :<%= attr %>
28
- it { expect_error_with_missing_attribute(:<%= attr %>) }
29
- xit { expect_error_with_attribute_value(:<%= attr %>, <%= attr.upcase %>_BAD_VALUE, message: nil) } # :message is optional
30
- xit { expect_valid_with_attribute_value(:<%= attr %>, <%= attr.upcase %>_GOOD_VALUE) }
25
+ context '#<%= attr %>' do
26
+ # Attribute: :<%= attr %>
27
+ it { expect_error_with_missing_attribute(:<%= attr %>) }
28
+ xit { expect_error_with_attribute_value(:<%= attr %>, <%= attr.upcase %>_BAD_VALUE, message: nil) } # :message is optional
29
+ xit { expect_valid_with_attribute_value(:<%= attr %>, <%= attr.upcase %>_GOOD_VALUE) }
30
+ end
31
31
 
32
32
  <% end -%>
33
- ### END: Attribute Expectations
34
-
35
33
  # Other Expectation Helpers
36
34
  # xit { expect_error_message(message) }
37
35
  # xit { expect_error_with_attribute(attribute) }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: formify
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.15.0
4
+ version: 0.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Jackson
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: with_advisory_lock
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
83
97
  description: Formify acts as an abstract class, allowing you to easily create robust
84
98
  form objects and test them using rspec.
85
99
  email: