whiteprint 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +196 -0
- data/lib/tasks/{blueprint.rake → whiteprint.rake} +0 -0
- data/lib/whiteprint.rb +21 -0
- data/lib/whiteprint/adapters/active_record.rb +13 -1
- data/lib/whiteprint/attributes.rb +12 -7
- data/lib/whiteprint/config.rb +1 -1
- data/lib/whiteprint/migrator.rb +38 -31
- data/lib/whiteprint/railtie.rb +0 -10
- data/lib/whiteprint/version.rb +1 -1
- data/test/cases/migrator_test.rb +2 -5
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cef8249f6ad8c0449c9a9348f5d57f08e5ff552e
|
4
|
+
data.tar.gz: 64abf99090a1f2b2cb26e91c46e7f27eb9fd3698
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9da7bbb71e51c8831a685ecccb948870618bb027b3f4316e423c9821e783482c56da0b081f5ab15d0841327a31d42d49b886dc291e9289ddf6d0db28293bcc0d
|
7
|
+
data.tar.gz: 54f8d2b975b4afcb6e6eebff5f4bd05ce63df83515ae98afde32974bc950de3806b7bc4b7c56b03e1a1f427e2af79e4a1789ae0c1fcb7c067f241bfa6c06070d
|
data/README.md
CHANGED
@@ -241,11 +241,207 @@ end
|
|
241
241
|
|
242
242
|
### Method as default value
|
243
243
|
|
244
|
+
You can specify a symbol as the default value for an attribute for dynamic defaults.
|
245
|
+
|
246
|
+
```ruby
|
247
|
+
class Car < ActiveRecord::Base
|
248
|
+
include Whiteprint::Model
|
249
|
+
|
250
|
+
whiteprint do
|
251
|
+
references :user, default: :current_user
|
252
|
+
end
|
253
|
+
|
254
|
+
private
|
255
|
+
|
256
|
+
def current_user
|
257
|
+
User.current
|
258
|
+
end
|
259
|
+
end
|
260
|
+
```
|
261
|
+
|
244
262
|
### Accessor
|
245
263
|
|
264
|
+
You can use the accessor type to add attr_accessors to your model
|
265
|
+
|
266
|
+
```ruby
|
267
|
+
class User < ActiveRecord::Base
|
268
|
+
include Whiteprint::Model
|
269
|
+
|
270
|
+
whiteprint do
|
271
|
+
text :password_digest
|
272
|
+
accessor :password
|
273
|
+
accessor :password_confirmation
|
274
|
+
end
|
275
|
+
end
|
276
|
+
```
|
277
|
+
|
246
278
|
## Attributes
|
247
279
|
|
280
|
+
The whiteprint instance of a model can be accessed using the whiteprint method: `Model.whiteprint`. The attributes of a whiteprint are available using the attributes method: `Model.whiteprint.attributes`.
|
281
|
+
These attributes are an instance of `Whiteprint::Attributes` and have several helper methods available.
|
282
|
+
|
283
|
+
### for_serializer
|
284
|
+
|
285
|
+
The for_serializer helper lists all attributes that aren't private or associations.
|
286
|
+
|
287
|
+
```ruby
|
288
|
+
class Car < ActiveRecord::Base
|
289
|
+
include Whiteprint::Model
|
290
|
+
|
291
|
+
whiteprint do
|
292
|
+
string :brand, default: 'Ford'
|
293
|
+
string :name
|
294
|
+
decimal :price, precision: 10, scale: 5, private: true
|
295
|
+
references :color
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
Car.whiteprint.attributes.for_serializer
|
300
|
+
# [:id, :created_at, :updated_at, :brand, :name]
|
301
|
+
|
302
|
+
# usage example
|
303
|
+
class CarSerializer < ActiveModel::Serializer
|
304
|
+
attributes *Car.whiteprint.attributes.for_serializer
|
305
|
+
end
|
306
|
+
```
|
307
|
+
|
308
|
+
### for_permitted
|
309
|
+
|
310
|
+
The for_serializer helper lists all attributes that aren't private or readonly in a format suitable for Rails' strong paramters.
|
311
|
+
|
312
|
+
```ruby
|
313
|
+
class Car < ActiveRecord::Base
|
314
|
+
include Whiteprint::Model
|
315
|
+
|
316
|
+
whiteprint do
|
317
|
+
string :brand, default: 'Ford'
|
318
|
+
string :name, readonly: true
|
319
|
+
text :specs, array: true
|
320
|
+
decimal :price, precision: 10, scale: 5, private: true
|
321
|
+
references :color
|
322
|
+
habtm :owners, class_name: 'User'
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
Car.whiteprint.attributes.for_permitted
|
327
|
+
# [:id, :brand, {:specs=>[]}, :color_id, {:owner_ids=>[]}]
|
328
|
+
|
329
|
+
# usage example
|
330
|
+
def permitted_params
|
331
|
+
params.require(:car).permit(*Car.whiteprint.attributes.for_permitted)
|
332
|
+
end
|
333
|
+
```
|
334
|
+
|
335
|
+
### for_meta
|
336
|
+
|
337
|
+
The for_meta helper lists all meta info that is specified for attributes. The `meta_attribute_options` config determines which options should be listed by this helper.
|
338
|
+
|
339
|
+
```ruby
|
340
|
+
Whiteprint.config do |c|
|
341
|
+
c.meta_attribute_options = [:enum, :label]
|
342
|
+
end
|
343
|
+
|
344
|
+
class Car < ActiveRecord::Base
|
345
|
+
include Whiteprint::Model
|
346
|
+
|
347
|
+
whiteprint do
|
348
|
+
string :brand, default: 'Ford', enum: {"Ford"=>"Ford", "BMW"=>"BMW", "Audi"=>"Audi"}, label: 'Merk'
|
349
|
+
string :name, label: 'Naam'
|
350
|
+
text :specs, array: true
|
351
|
+
decimal :price, precision: 10, scale: 5, private: true
|
352
|
+
references :color
|
353
|
+
habtm :owners, class_name: 'User'
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
Car.whiteprint.attributes.for_meta
|
358
|
+
# {:brand=>{:enum=>{"Ford"=>"Ford", "BMW"=>"BMW", "Audi"=>"Audi"}, :label=>"Merk"}, :name=>{:label=>"Naam"}}
|
359
|
+
|
360
|
+
# example usage
|
361
|
+
render json: @car, meta: Car.whiteprint.attributes.for_meta
|
362
|
+
```
|
363
|
+
|
364
|
+
## Composability
|
365
|
+
|
366
|
+
Whiteprints are inherited and can be composed.
|
367
|
+
|
368
|
+
```ruby
|
369
|
+
class Animal
|
370
|
+
include Whiteprint::Model
|
371
|
+
|
372
|
+
whiteprint do
|
373
|
+
text :name
|
374
|
+
text :description
|
375
|
+
end
|
376
|
+
end
|
377
|
+
|
378
|
+
module Mammal
|
379
|
+
extend ActiveSupport::Concern
|
380
|
+
|
381
|
+
included do
|
382
|
+
whiteprint do
|
383
|
+
integer :gestation_period
|
384
|
+
end
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
class Dog < Animal
|
389
|
+
include Mammal
|
390
|
+
|
391
|
+
whiteprint do
|
392
|
+
string :breed
|
393
|
+
end
|
394
|
+
end
|
395
|
+
|
396
|
+
class Cat < Animal
|
397
|
+
include Mammal
|
398
|
+
|
399
|
+
whiteprint do
|
400
|
+
boolean :domestic, default: true
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
Cat.whiteprint.attributes.to_a.map(&:to_h)
|
405
|
+
# [{:name=>:name, :type=>:text}, {:name=>:description, :type=>:text}, {:name=>:gestation_period, :type=>:integer}, {:name=>:domestic, :type=>:boolean, :default=>true}]
|
406
|
+
|
407
|
+
Dog.whiteprint.attributes.to_a.map(&:to_h)
|
408
|
+
#[{:name=>:name, :type=>:text}, {:name=>:description, :type=>:text}, {:name=>:gestation_period, :type=>:integer}, {:name=>:breed, :type=>:string}]
|
409
|
+
```
|
410
|
+
|
248
411
|
## Configuration
|
249
412
|
|
413
|
+
```ruby
|
414
|
+
Whiteprint.config do |c|
|
415
|
+
# which adapter to use if none is applicable
|
416
|
+
c.default_adapter = :base
|
417
|
+
|
418
|
+
# Models have to be loaded before whiteprint:migrate runs. Set to true to let Whiteprint do this for you.
|
419
|
+
c.eager_load = false # default true for Rails projects
|
420
|
+
|
421
|
+
# Define which path(s) contain whiteprint models
|
422
|
+
c.eager_load_paths = []
|
423
|
+
|
424
|
+
# Define which attribute options are persisted
|
425
|
+
c.persisted_attribute_options = {
|
426
|
+
array: false,
|
427
|
+
limit: nil,
|
428
|
+
precision: nil,
|
429
|
+
scale: nil,
|
430
|
+
polymorphic: false,
|
431
|
+
null: true,
|
432
|
+
default: nil
|
433
|
+
}
|
434
|
+
|
435
|
+
# Define the attribute options for the for_meta gelper
|
436
|
+
c.meta_attribute_options = [:enum]
|
437
|
+
|
438
|
+
# Define if changes should be run in a single or separately migrations. One of: :ask, :separately, :together
|
439
|
+
c.migration_strategy = :ask
|
440
|
+
|
441
|
+
# Define if migrations should be automatically added to git
|
442
|
+
c.add_migration_to_git = false
|
443
|
+
end
|
444
|
+
```
|
445
|
+
|
250
446
|
## Origin
|
251
447
|
Whiteprint is extracted from an application framework we use internally. Right now, our framework is lacking tests and documentation, but we intend to open source more parts of our framework in the future.
|
File without changes
|
data/lib/whiteprint.rb
CHANGED
@@ -30,6 +30,8 @@ module Whiteprint
|
|
30
30
|
default: nil
|
31
31
|
}
|
32
32
|
c.meta_attribute_options = [:enum]
|
33
|
+
c.migration_strategy = :ask
|
34
|
+
c.add_migration_to_git = false
|
33
35
|
end
|
34
36
|
|
35
37
|
if defined?(ActiveRecord)
|
@@ -88,6 +90,25 @@ module Whiteprint
|
|
88
90
|
whiteprints.select(&:changes?)
|
89
91
|
end
|
90
92
|
|
93
|
+
def migrate(cli, separately:)
|
94
|
+
changed_whiteprints.group_by(&:transformer).map do |adapter, whiteprints|
|
95
|
+
if separately
|
96
|
+
cli.say 'Processing as separate migrations...'
|
97
|
+
whiteprints.each do |whiteprint|
|
98
|
+
cli.say whiteprint.explanation
|
99
|
+
migration_path = adapter.generate_migration(*adapter.migration_params(cli), [whiteprint.changes_tree])
|
100
|
+
`git add #{migration_path}` if Whiteprint.config.add_migration_to_git
|
101
|
+
end
|
102
|
+
else
|
103
|
+
cli.say 'Processing as a single migration...'
|
104
|
+
migration_path = adapter.generate_migration(*adapter.migration_params(cli), whiteprints.map(&:changes_tree))
|
105
|
+
`git add #{migration_path}` if Whiteprint.config.add_migration_to_git
|
106
|
+
end
|
107
|
+
|
108
|
+
adapter.migrate
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
91
112
|
def plugins
|
92
113
|
@@plugins
|
93
114
|
end
|
@@ -27,15 +27,27 @@ module Whiteprint
|
|
27
27
|
|
28
28
|
def generate_migration(name, trees)
|
29
29
|
filename = "#{Time.now.strftime('%Y%m%d%H%M%S')}_#{underscore(name)}.rb"
|
30
|
-
File.
|
30
|
+
path = File.join(Whiteprint.config.migration_path, filename)
|
31
|
+
File.open(path, 'w') do |f|
|
31
32
|
f.write migration(name, trees)
|
32
33
|
end
|
34
|
+
path
|
35
|
+
end
|
36
|
+
|
37
|
+
def migrate
|
38
|
+
::ActiveRecord::Migration.verbose = true
|
39
|
+
::ActiveRecord::Migrator.migrate(::ActiveRecord::Migrator.migrations_paths)
|
33
40
|
end
|
34
41
|
|
35
42
|
def migration(name, trees)
|
36
43
|
"class #{camelize(name)} < ActiveRecord::Migration\n def change\n" + transform(trees) + " end\nend\n"
|
37
44
|
end
|
38
45
|
|
46
|
+
def migration_params(cli)
|
47
|
+
name = cli.ask 'How would you like to name this migration?'
|
48
|
+
[name]
|
49
|
+
end
|
50
|
+
|
39
51
|
private
|
40
52
|
|
41
53
|
def transform(trees)
|
@@ -36,7 +36,7 @@ module Whiteprint
|
|
36
36
|
def for_meta(instance)
|
37
37
|
::Whiteprint.config.meta_attribute_options.map do |option|
|
38
38
|
{option => send("meta_#{option}", instance)}
|
39
|
-
end.inject(&:merge).
|
39
|
+
end.inject(&:merge).select { |_, value| !value.nil? }
|
40
40
|
end
|
41
41
|
|
42
42
|
def for_persisted(**config)
|
@@ -55,6 +55,7 @@ module Whiteprint
|
|
55
55
|
enum
|
56
56
|
end
|
57
57
|
|
58
|
+
return nil unless _enum
|
58
59
|
return _enum if _enum.is_a?(Hash)
|
59
60
|
|
60
61
|
_enum.map do |value|
|
@@ -62,7 +63,7 @@ module Whiteprint
|
|
62
63
|
end.inject(&:merge)
|
63
64
|
end
|
64
65
|
|
65
|
-
def method_missing(name)
|
66
|
+
def method_missing(name, *args)
|
66
67
|
if name.to_s.starts_with?('meta_')
|
67
68
|
self[name.to_s.remove(/^meta_/)]
|
68
69
|
else
|
@@ -159,24 +160,28 @@ module Whiteprint
|
|
159
160
|
persisted_scope
|
160
161
|
end
|
161
162
|
|
162
|
-
def for_meta(instance)
|
163
|
+
def for_meta(instance = nil)
|
163
164
|
where(::Whiteprint.config.meta_attribute_options).to_h.map do |key, attribute|
|
164
165
|
{key => attribute.for_meta(instance)}
|
165
166
|
end.inject(&:merge)
|
166
167
|
end
|
167
168
|
|
168
169
|
def for_serializer
|
169
|
-
|
170
|
+
# TODO: move specifics to activerecord adapater
|
171
|
+
|
172
|
+
self.not(type: :references).not(type: :has_and_belongs_to_many).not(private: true).keys
|
170
173
|
end
|
171
174
|
|
172
175
|
def for_permitted
|
173
|
-
|
176
|
+
# TODO: move specifics to activerecord adapater
|
177
|
+
|
178
|
+
self.not(readonly: true).not(private: true).not(name: [:updated_at, :created_at]).to_h.map do |name, attribute|
|
174
179
|
if attribute.array
|
175
180
|
{name => []}
|
176
181
|
elsif attribute.type == :has_and_belongs_to_many
|
177
|
-
{"#{name.to_s.singularize}_ids" => []}
|
182
|
+
{:"#{name.to_s.singularize}_ids" => []}
|
178
183
|
elsif attribute.type == :references
|
179
|
-
"#{attribute.name}_id"
|
184
|
+
:"#{attribute.name}_id"
|
180
185
|
else
|
181
186
|
name
|
182
187
|
end
|
data/lib/whiteprint/config.rb
CHANGED
@@ -7,7 +7,7 @@ module Whiteprint
|
|
7
7
|
module Config
|
8
8
|
class << self
|
9
9
|
attr_accessor :default_adapter, :persisted_attribute_options, :eager_load, :eager_load_paths,
|
10
|
-
:migration_path, :meta_attribute_options, :plugins
|
10
|
+
:migration_path, :meta_attribute_options, :plugins, :migration_strategy, :add_migration_to_git
|
11
11
|
|
12
12
|
def plugin(name)
|
13
13
|
self.plugins << name
|
data/lib/whiteprint/migrator.rb
CHANGED
@@ -1,5 +1,25 @@
|
|
1
1
|
module Whiteprint
|
2
2
|
module Migrator
|
3
|
+
class Cli
|
4
|
+
def initialize(input, output)
|
5
|
+
@cli = HighLine.new input, output
|
6
|
+
end
|
7
|
+
|
8
|
+
def ask(*args)
|
9
|
+
@cli.ask(*args)
|
10
|
+
end
|
11
|
+
|
12
|
+
def choose(*args, &block)
|
13
|
+
@cli.choose(*args, &block)
|
14
|
+
end
|
15
|
+
|
16
|
+
def say(*messages)
|
17
|
+
messages.each do |message|
|
18
|
+
@cli.say(message)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
3
23
|
class << self
|
4
24
|
def eager_load!
|
5
25
|
return unless Whiteprint.config.eager_load
|
@@ -19,45 +39,32 @@ module Whiteprint
|
|
19
39
|
end
|
20
40
|
end
|
21
41
|
|
22
|
-
def
|
23
|
-
|
42
|
+
def no_changes?
|
43
|
+
number_of_changes == 0
|
44
|
+
end
|
24
45
|
|
46
|
+
def interactive(input: $stdin, output: $stdout)
|
25
47
|
eager_load!
|
26
|
-
cli =
|
48
|
+
cli = Cli.new(input, output)
|
27
49
|
|
28
|
-
if
|
29
|
-
|
30
|
-
return
|
31
|
-
end
|
50
|
+
# return if there are no changes
|
51
|
+
cli.say('Whiteprint detected no changes') and return if no_changes?
|
32
52
|
|
33
|
-
|
34
|
-
|
35
|
-
cli.say explanation
|
36
|
-
end
|
53
|
+
# list all changes
|
54
|
+
cli.say "Whiteprint has detected <%= color('#{number_of_changes}', :bold, :white) %> changes to your models.", *explanations
|
37
55
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
56
|
+
if Whiteprint.config.migration_strategy == :ask
|
57
|
+
cli.choose do |menu|
|
58
|
+
menu.header = 'Migrations'
|
59
|
+
menu.prompt = 'How would you like to process these changes?'
|
60
|
+
menu.choice('In one migration') { Whiteprint.migrate cli, separately: false }
|
61
|
+
menu.choice('In separate migrations') { Whiteprint.migrate cli, separately: true }
|
62
|
+
end
|
63
|
+
else
|
64
|
+
Whiteprint.migrate cli, separately: (Whiteprint.config.migration_strategy == :separately)
|
43
65
|
end
|
44
66
|
end
|
45
67
|
|
46
|
-
def migrate_at_once(input: $stdin, output: $stdout)
|
47
|
-
# TODO: Clean up
|
48
|
-
|
49
|
-
cli = HighLine.new input, output
|
50
|
-
name = cli.ask 'How would you like to name this migration?'
|
51
|
-
Whiteprint.changed_whiteprints
|
52
|
-
.group_by(&:transformer)
|
53
|
-
.map do |adapter, whiteprints|
|
54
|
-
adapter.generate_migration(name, whiteprints.map(&:changes_tree))
|
55
|
-
end
|
56
|
-
|
57
|
-
ActiveRecord::Migration.verbose = true
|
58
|
-
ActiveRecord::Migrator.migrate(ActiveRecord::Migrator.migrations_paths)
|
59
|
-
end
|
60
|
-
|
61
68
|
def number_of_changes
|
62
69
|
Whiteprint.changed_whiteprints.size
|
63
70
|
end
|
data/lib/whiteprint/railtie.rb
CHANGED
@@ -1,14 +1,5 @@
|
|
1
1
|
module Whiteprint
|
2
2
|
class Railtie < Rails::Railtie
|
3
|
-
class << self
|
4
|
-
def whiteprint_config
|
5
|
-
::Whiteprint.config do |c|
|
6
|
-
c.eager_load = true
|
7
|
-
c.migration_path = Rails.root.join(ActiveRecord::Migrator.migrations_path)
|
8
|
-
end
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
3
|
initializer "whiteprint.config_for_rails" do
|
13
4
|
::Whiteprint.config do |c|
|
14
5
|
c.eager_load = true
|
@@ -17,7 +8,6 @@ module Whiteprint
|
|
17
8
|
end
|
18
9
|
|
19
10
|
rake_tasks do
|
20
|
-
# whiteprint_config
|
21
11
|
load "tasks/whiteprint.rake"
|
22
12
|
end
|
23
13
|
end
|
data/lib/whiteprint/version.rb
CHANGED
data/test/cases/migrator_test.rb
CHANGED
@@ -50,13 +50,10 @@ end
|
|
50
50
|
|
51
51
|
input = StringIO.new
|
52
52
|
input << '1' << "\n"
|
53
|
+
input << 'test migration' << "\n"
|
53
54
|
input.rewind
|
54
55
|
|
55
|
-
|
56
|
-
migrate_input << 'test migration' << "\n"
|
57
|
-
migrate_input.rewind
|
58
|
-
|
59
|
-
Whiteprint::Migrator.interactive input: input, migrate_input: migrate_input
|
56
|
+
Whiteprint::Migrator.interactive input: input
|
60
57
|
|
61
58
|
migration = File.read(Dir.glob('test/db/migrate/*_test_migration.rb').first)
|
62
59
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: whiteprint
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ewout Kleinsmann
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2016-09-
|
12
|
+
date: 2016-09-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: parslet
|
@@ -204,7 +204,7 @@ extra_rdoc_files: []
|
|
204
204
|
files:
|
205
205
|
- README.md
|
206
206
|
- Rakefile
|
207
|
-
- lib/tasks/
|
207
|
+
- lib/tasks/whiteprint.rake
|
208
208
|
- lib/whiteprint.rb
|
209
209
|
- lib/whiteprint/adapters/active_record.rb
|
210
210
|
- lib/whiteprint/adapters/active_record/has_and_belongs_to_many.rb
|