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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ac5d879aa5d240df5fb1689636e8c15462f9a192
4
- data.tar.gz: 464a364c30c9edc45f9418d4f1f9ab8eaf57fb43
3
+ metadata.gz: cef8249f6ad8c0449c9a9348f5d57f08e5ff552e
4
+ data.tar.gz: 64abf99090a1f2b2cb26e91c46e7f27eb9fd3698
5
5
  SHA512:
6
- metadata.gz: 78358814f2b3e688cdb5720a4049c3bd3b0951d83f74627aaa754688ffa2b9792d5c8d44de038b824f44b785c55046b69299d84048a932f5157cfd223dde64f4
7
- data.tar.gz: 2304c58e46b558e2833ebb1fe8d8aafed433e060a4f62afb814a1ea95389e5ab5793cdb82f04c06bee692bf8c9f753851b97325d299aaf25047eeef2d48bcc50
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.
@@ -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.open(File.join(Whiteprint.config.migration_path, filename), 'w') do |f|
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).compact
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
- self.not(type: :references).not(private: true).keys
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
- self.not(readonly: true).not(name: [:updated_at, :created_at]).to_h.map do |name, attribute|
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
@@ -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
@@ -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 interactive(input: $stdin, output: $stdout, migrate_input: $stdin, migrate_output: $stdout)
23
- # TODO: Clean up
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 = HighLine.new input, output
48
+ cli = Cli.new(input, output)
27
49
 
28
- if number_of_changes == 0
29
- cli.say('Whiteprint detected no changes')
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
- cli.say "Whiteprint has detected <%= color('#{number_of_changes}', :bold, :white) %> changes to your models."
34
- explanations.each do |explanation|
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
- cli.choose do |menu|
39
- menu.header = 'Migrations'
40
- menu.prompt = 'How would you like to process these changes?'
41
- menu.choice('In one migration') { migrate_at_once(input: migrate_input, output: migrate_output) }
42
- menu.choice('In separate migrations') { cli.say 'Bar' }
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
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Whiteprint
2
- VERSION = '0.1.0'.freeze
2
+ VERSION = '0.1.1'.freeze
3
3
  end
@@ -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
- migrate_input = StringIO.new
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.0
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-05 00:00:00.000000000 Z
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/blueprint.rake
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