praxis-blueprints 3.2 → 3.3

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: e3679f890d11a1474437b9018c7030a909e050ca
4
- data.tar.gz: 61094297c27f94f4add6227d637654fae03ed662
3
+ metadata.gz: 2baa109dab4f8585d61ee9d5317bc11039cbaa98
4
+ data.tar.gz: f2bd553cd4f075311155154b0c7fef9aad415b3f
5
5
  SHA512:
6
- metadata.gz: aa633b4772c07f859fd4024044a618c2243b5b6d95678dd7c0757486811dc1a315eafde12319c4c4215b72571d0d38f7ccdae7d91903183aa14c1baaaa86ae58
7
- data.tar.gz: 47e3c62fd5bdfa05c142083e7053687d57d25fd2b08b3a5b2dd913050cbaf94a9ccfb699707e1c1e43f6384ce25e4688beaed9cb81a39038080eff2155e9c979
6
+ metadata.gz: e52a91a7f9c78b9964650972b09c61a883035903f04e41374e82115d04b6643776f9ff05aebb281a007b7074913c9bfb086328ab6043418bd0df0ec8232ccfc8
7
+ data.tar.gz: af137419cbb73cceac9e4283a2558d758b0f4425ac009fe9243780328eb6df1091cdcee776147327fb646630a1832573bdbbd7e14b5a9cac8cb4e30d2f31fa5a
@@ -0,0 +1,35 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.3
3
+ Style/Documentation:
4
+ Enabled: false
5
+ Metrics/MethodLength:
6
+ Enabled: false
7
+ Metrics/ClassLength:
8
+ Enabled: false
9
+ Metrics/LineLength:
10
+ Max: 200
11
+ Style/RedundantSelf:
12
+ Enabled: false
13
+ Style/ClassAndModuleChildren:
14
+ Enabled: false
15
+ Lint/Debugger:
16
+ Enabled: false
17
+ Metrics/AbcSize:
18
+ Enabled: false
19
+ Style/CaseEquality:
20
+ Enabled: false
21
+ Lint/UnusedMethodArgument:
22
+ AllowUnusedKeywordArguments: true
23
+ Style/FileName:
24
+ Exclude:
25
+ - 'lib/praxis-blueprints.rb'
26
+ Style/ClassVars:
27
+ Exclude:
28
+ - 'lib/praxis-blueprints/blueprint.rb'
29
+
30
+ # Offense count: 5
31
+ Metrics/CyclomaticComplexity:
32
+ Max: 8
33
+ # Offense count: 5
34
+ Metrics/PerceivedComplexity:
35
+ Max: 12
@@ -2,6 +2,8 @@ language: ruby
2
2
  cache: bundler
3
3
  sudo: false
4
4
  rvm:
5
- - 2.1.2
6
- - 2.2.2
7
- script: bundle exec rspec spec
5
+ - 2.2.5
6
+ - 2.3.1
7
+ script:
8
+ - bundle exec rspec spec
9
+ - bundle exec rubocop --format=clang
@@ -2,6 +2,12 @@
2
2
 
3
3
  ## next
4
4
 
5
+ ## 3.3
6
+
7
+ * Include Attributor::Dumpable in Blueprint, so it renders (semi) correctly if
8
+ rendered with just `true` specified for fields.
9
+ * Fix bug rendering subobjects with nil values (manifested when `include_nil: true` there’s an explicit subsection of fields)
10
+
5
11
  ## 3.2
6
12
 
7
13
  * Ensure we call `object.dump` in Renderer when fully dumping an instance (or array of instances) that have the Attributor::Dumpable module (i.e., when no subfields were selected)
data/Gemfile CHANGED
@@ -1,2 +1,3 @@
1
+ # frozen_string_literal: true
1
2
  source 'https://rubygems.org'
2
3
  gemspec
data/Guardfile CHANGED
@@ -1,11 +1,17 @@
1
+ # frozen_string_literal: true
1
2
  # Config file for Guard
2
3
  # More info at https://github.com/guard/guard#readme
4
+ group :red_green_refactor, halt_on_fail: true do
5
+ guard :rspec, cmd: 'bundle exec rspec' do
6
+ watch(%r{^spec/.+_spec\.rb$})
7
+ watch(%r{^lib/praxis-blueprints/(.+)\.rb$}) { |m| "spec/praxis-blueprints/#{m[1]}_spec.rb" }
8
+ watch('spec/*.rb') { 'spec' }
9
+ watch('lib/praxis-blueprints.rb') { 'spec' }
10
+ watch(%r{^spec/support/(.+)\.rb$}) { 'spec' }
11
+ end
3
12
 
4
- guard :rspec, cmd: 'bundle exec rspec' do
5
- watch(%r{^spec/.+_spec\.rb$})
6
- watch(%r{^lib/praxis-blueprints/(.+)\.rb$}) { |m| "spec/praxis-blueprints/#{m[1]}_spec.rb" }
7
- watch('spec/*.rb') { 'spec' }
8
- watch('lib/praxis-blueprints.rb') { 'spec' }
9
- watch(%r{^spec/support/(.+)\.rb$}) { 'spec' }
13
+ guard :rubocop, cli: '--auto-correct --display-cop-names' do
14
+ watch(/.+\.rb$/)
15
+ watch(%r{(?:.+/)?\.rubocop\.yml$}) { |m| File.dirname(m[0]) }
16
+ end
10
17
  end
11
-
data/Rakefile CHANGED
@@ -1,17 +1,18 @@
1
+ # frozen_string_literal: true
1
2
  require 'bundler/setup'
2
3
 
3
- require "bundler/gem_tasks"
4
+ require 'bundler/gem_tasks'
4
5
 
5
6
  require 'rspec/core'
6
7
  require 'rspec/core/rake_task'
7
8
  require 'bundler/gem_tasks'
8
9
 
9
- desc "Run RSpec code examples with simplecov"
10
+ desc 'Run RSpec code examples with simplecov'
10
11
  RSpec::Core::RakeTask.new do |spec|
11
12
  spec.pattern = FileList['spec/**/*_spec.rb']
12
13
  end
13
14
 
14
- task :default => :spec
15
+ task default: :spec
15
16
 
16
17
  require 'yard'
17
18
  YARD::Rake::YardocTask.new
@@ -1,10 +1,11 @@
1
+ # frozen_string_literal: true
1
2
  require 'json'
2
3
  require 'yaml'
3
4
  require 'logger'
4
5
 
5
6
  require 'attributor'
6
7
 
7
- require "praxis-blueprints/version"
8
+ require 'praxis-blueprints/version'
8
9
 
9
10
  require 'praxis-blueprints/finalizable'
10
11
  require 'praxis-blueprints/config_hash'
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  require 'ostruct'
2
3
 
3
4
  # Blueprint ==
@@ -7,6 +8,8 @@ require 'ostruct'
7
8
  module Praxis
8
9
  class Blueprint
9
10
  include Attributor::Type
11
+ include Attributor::Dumpable
12
+
10
13
  extend Finalizable
11
14
 
12
15
  if RUBY_ENGINE =~ /^jruby/
@@ -18,7 +21,6 @@ module Praxis
18
21
 
19
22
  @@caching_enabled = false
20
23
 
21
-
22
24
  attr_reader :validating
23
25
  attr_accessor :object
24
26
  attr_accessor :decorators
@@ -34,20 +36,20 @@ module Praxis
34
36
  super
35
37
 
36
38
  klass.instance_eval do
37
- @views = Hash.new
38
- @options = Hash.new
39
+ @views = {}
40
+ @options = {}
39
41
  @domain_model = Object
40
42
  end
41
43
  end
42
44
 
43
45
  # Override default new behavior to support memoized creation through an IdentityMap
44
- def self.new(object, decorators=nil)
46
+ def self.new(object, decorators = nil)
45
47
  if @@caching_enabled && decorators.nil?
46
48
  cache = if object.respond_to?(:identity_map) && object.identity_map
47
- object.identity_map.blueprint_cache[self]
48
- else
49
- self.cache
50
- end
49
+ object.identity_map.blueprint_cache[self]
50
+ else
51
+ self.cache
52
+ end
51
53
 
52
54
  return cache[object] ||= begin
53
55
  blueprint = self.allocate
@@ -65,14 +67,12 @@ module Praxis
65
67
  'hash'
66
68
  end
67
69
 
68
- def self.describe(shallow=false,example: nil, **opts)
70
+ def self.describe(shallow = false, example: nil, **opts)
69
71
  type_name = self.ancestors.find { |k| k.name && !k.name.empty? }.name
70
72
 
71
- if example
72
- example = example.object
73
- end
73
+ example = example.object if example
74
74
 
75
- description = self.attribute.type.describe(shallow,example: example, **opts).merge!(id: self.id, name: type_name)
75
+ description = self.attribute.type.describe(shallow, example: example, **opts).merge!(id: self.id, name: type_name)
76
76
  description.delete :anonymous # discard the Struct's view of anonymity, and use the Blueprint's one
77
77
  description[:anonymous] = @_anonymous unless @_anonymous.nil?
78
78
 
@@ -85,37 +85,30 @@ module Praxis
85
85
  description
86
86
  end
87
87
 
88
-
89
- def self.attributes(opts={}, &block)
88
+ def self.attributes(opts = {}, &block)
90
89
  if block_given?
91
- if self.const_defined?(:Struct, false)
92
- raise "Redefining Blueprint attributes is not currently supported"
93
- else
90
+ raise 'Redefining Blueprint attributes is not currently supported' if self.const_defined?(:Struct, false)
94
91
 
95
- if opts.has_key?(:reference) && opts[:reference] != self.reference
96
- raise "Reference mismatch in #{self.inspect}. Given :reference option #{opts[:reference].inspect}, while using #{self.reference.inspect}"
97
- elsif self.reference
98
- opts[:reference] = self.reference #pass the reference Class down
99
- else
100
- opts[:reference] = self
101
- end
102
-
103
- @options.merge!(opts)
104
- @block = block
92
+ if opts.key?(:reference) && opts[:reference] != self.reference
93
+ raise "Reference mismatch in #{self.inspect}. Given :reference option #{opts[:reference].inspect}, while using #{self.reference.inspect}"
94
+ elsif self.reference
95
+ opts[:reference] = self.reference # pass the reference Class down
96
+ else
97
+ opts[:reference] = self
105
98
  end
106
99
 
100
+ @options.merge!(opts)
101
+ @block = block
102
+
107
103
  return @attribute
108
104
  end
109
105
 
110
- unless @attribute
111
- raise "@attribute not defined yet for #{self.name}"
112
- end
106
+ raise "@attribute not defined yet for #{self.name}" unless @attribute
113
107
 
114
108
  @attribute.attributes
115
109
  end
116
110
 
117
-
118
- def self.domain_model(klass=nil)
111
+ def self.domain_model(klass = nil)
119
112
  return @domain_model if klass.nil?
120
113
  @domain_model = klass
121
114
  end
@@ -123,26 +116,25 @@ module Praxis
123
116
  def self.check_option!(name, value)
124
117
  case name
125
118
  when :identity
126
- raise Attributor::AttributorException, "Invalid identity type #{value.inspect}" unless value.kind_of?(::Symbol)
119
+ raise Attributor::AttributorException, "Invalid identity type #{value.inspect}" unless value.is_a?(::Symbol)
127
120
  return :ok
128
121
  else
129
122
  return Attributor::Struct.check_option!(name, value)
130
123
  end
131
124
  end
132
125
 
133
-
134
- def self.load(value,context=Attributor::DEFAULT_ROOT_CONTEXT, **options)
126
+ def self.load(value, context = Attributor::DEFAULT_ROOT_CONTEXT, **options)
135
127
  case value
136
128
  when self
137
129
  value
138
130
  when nil, Hash, String
139
131
  # Need to parse/deserialize first
140
132
  # or apply default/recursive loading options if necessary
141
- if (value = self.attribute.load(value,context, **options))
133
+ if (value = self.attribute.load(value, context, **options))
142
134
  self.new(value)
143
135
  end
144
136
  else
145
- if value.kind_of?(self.domain_model) || value.kind_of?(self::Struct)
137
+ if value.is_a?(self.domain_model) || value.is_a?(self::Struct)
146
138
  # Wrap the value directly
147
139
  self.new(value)
148
140
  else
@@ -153,7 +145,7 @@ module Praxis
153
145
  end
154
146
 
155
147
  class << self
156
- alias_method :from, :load
148
+ alias from load
157
149
  end
158
150
 
159
151
  def self.caching_enabled?
@@ -175,35 +167,33 @@ module Praxis
175
167
 
176
168
  def self.valid_type?(value)
177
169
  # FIXME: this should be more... ducklike
178
- value.kind_of?(self) || value.kind_of?(self.attribute.type)
170
+ value.is_a?(self) || value.is_a?(self.attribute.type)
179
171
  end
180
172
 
181
- def self.example(context=nil, **values)
173
+ def self.example(context = nil, **values)
182
174
  context = case context
183
- when nil
184
- ["#{self.name}-#{values.object_id.to_s}"]
185
- when ::String
186
- [context]
187
- else
188
- context
189
- end
175
+ when nil
176
+ ["#{self.name}-#{values.object_id}"]
177
+ when ::String
178
+ [context]
179
+ else
180
+ context
181
+ end
190
182
 
191
183
  self.new(self.attribute.example(context, values: values))
192
184
  end
193
185
 
194
-
195
- def self.validate(value, context=Attributor::DEFAULT_ROOT_CONTEXT, _attribute=nil)
196
- raise ArgumentError, "Invalid context received (nil) while validating value of type #{self.name}" if context == nil
186
+ def self.validate(value, context = Attributor::DEFAULT_ROOT_CONTEXT, _attribute = nil)
187
+ raise ArgumentError, "Invalid context received (nil) while validating value of type #{self.name}" if context.nil?
197
188
  context = [context] if context.is_a? ::String
198
189
 
199
- unless value.kind_of?(self)
190
+ unless value.is_a?(self)
200
191
  raise ArgumentError, "Error validating #{Attributor.humanize_context(context)} as #{self.name} for an object of type #{value.class.name}."
201
192
  end
202
193
 
203
194
  value.validate(context)
204
195
  end
205
196
 
206
-
207
197
  def self.view(name, **options, &block)
208
198
  if block_given?
209
199
  return self.views[name] = View.new(name, self, **options, &block)
@@ -219,7 +209,7 @@ module Praxis
219
209
  object.render(view: view, context: context, **opts)
220
210
  end
221
211
  class << self
222
- alias_method :render, :dump
212
+ alias render dump
223
213
  end
224
214
 
225
215
  # Internal finalize! logic
@@ -235,7 +225,7 @@ module Praxis
235
225
  end
236
226
 
237
227
  def self.resolve_domain_model!
238
- return unless self.domain_model.kind_of?(String)
228
+ return unless self.domain_model.is_a?(String)
239
229
 
240
230
  @domain_model = self.domain_model.constantize
241
231
  end
@@ -248,7 +238,7 @@ module Praxis
248
238
  end
249
239
 
250
240
  def self.define_readers!
251
- self.attributes.each do |name, attribute|
241
+ self.attributes.each do |name, _attribute|
252
242
  name = name.to_sym
253
243
 
254
244
  # Don't redefine existing methods
@@ -258,7 +248,6 @@ module Praxis
258
248
  end
259
249
  end
260
250
 
261
-
262
251
  def self.define_reader!(name)
263
252
  attribute = self.attributes[name]
264
253
  # TODO: profile and optimize
@@ -270,18 +259,16 @@ module Praxis
270
259
  @decorators.send(name)
271
260
  else
272
261
  value = @object.__send__(name)
273
- return value if value.nil? || value.kind_of?(attribute.type)
262
+ return value if value.nil? || value.is_a?(attribute.type)
274
263
  attribute.load(value)
275
264
  end
276
265
  end
277
266
  end
278
267
 
279
-
280
-
281
268
  def self.generate_master_view!
282
269
  attributes = self.attributes
283
270
  view :master do
284
- attributes.each do | name, attr |
271
+ attributes.each do |name, _attr|
285
272
  # Note: we can freely pass master view for attributes that aren't blueprint/containers because
286
273
  # their dump methods will ignore it (they always dump everything regardless)
287
274
  attribute name, view: :default
@@ -289,33 +276,29 @@ module Praxis
289
276
  end
290
277
  end
291
278
 
292
-
293
- def initialize(object, decorators=nil)
279
+ def initialize(object, decorators = nil)
294
280
  # TODO: decide what sort of type checking (if any) we want to perform here.
295
281
  @object = object
296
282
 
297
- @decorators = if decorators.kind_of?(Hash) && decorators.any?
298
- OpenStruct.new(decorators)
299
- else
300
- decorators
301
- end
283
+ @decorators = if decorators.is_a?(Hash) && decorators.any?
284
+ OpenStruct.new(decorators)
285
+ else
286
+ decorators
287
+ end
302
288
 
303
289
  @validating = false
304
290
  end
305
291
 
306
-
307
292
  # Render the wrapped data with the given view
308
- def render(view_name=nil, context: Attributor::DEFAULT_ROOT_CONTEXT,renderer: Renderer.new, **opts)
309
- if view_name != nil
310
- warn "DEPRECATED: please do not pass the view name as the first parameter in Blueprint.render, pass through the view: named param instead."
293
+ def render(view_name = nil, context: Attributor::DEFAULT_ROOT_CONTEXT, renderer: Renderer.new, **opts)
294
+ if !view_name.nil?
295
+ warn 'DEPRECATED: please do not pass the view name as the first parameter in Blueprint.render, pass through the view: named param instead.'
311
296
  elsif opts.key?(:view)
312
297
  view_name = opts[:view]
313
298
  end
314
299
 
315
300
  fields = opts[:fields]
316
- if view_name.nil? && fields.nil?
317
- view_name = :default
318
- end
301
+ view_name = :default if view_name.nil? && fields.nil?
319
302
 
320
303
  if view_name
321
304
  unless (view = self.class.views[view_name])
@@ -326,7 +309,7 @@ module Praxis
326
309
 
327
310
  # Accept a simple array of fields, and transform it to a 1-level hash with true values
328
311
  if fields.is_a? Array
329
- fields = fields.each_with_object({}) {|field, hash| hash[field] = true }
312
+ fields = fields.each_with_object({}) { |field, hash| hash[field] = true }
330
313
  end
331
314
 
332
315
  # expand fields
@@ -334,33 +317,31 @@ module Praxis
334
317
 
335
318
  renderer.render(self, expanded_fields, context: context)
336
319
  end
337
- alias_method :to_hash, :render
338
- alias_method :dump, :render
320
+ alias to_hash render
321
+ alias dump render
339
322
 
340
- def validate(context=Attributor::DEFAULT_ROOT_CONTEXT)
341
- raise ArgumentError, "Invalid context received (nil) while validating value of type #{self.name}" if context == nil
323
+ def validate(context = Attributor::DEFAULT_ROOT_CONTEXT)
324
+ raise ArgumentError, "Invalid context received (nil) while validating value of type #{self.name}" if context.nil?
342
325
  context = [context] if context.is_a? ::String
343
326
  keys_with_values = []
344
327
 
345
- raise "validation conflict" if @validating
328
+ raise 'validation conflict' if @validating
346
329
  @validating = true
347
330
 
348
331
  errors = []
349
332
  self.class.attributes.each do |sub_attribute_name, sub_attribute|
350
- sub_context = self.class.generate_subcontext(context,sub_attribute_name)
333
+ sub_context = self.class.generate_subcontext(context, sub_attribute_name)
351
334
  value = self.send(sub_attribute_name)
352
- unless value.nil?
353
- keys_with_values << sub_attribute_name
354
- end
335
+ keys_with_values << sub_attribute_name unless value.nil?
355
336
 
356
337
  if value.respond_to?(:validating) # really, it's a thing with sub-attributes
357
338
  next if value.validating
358
339
  end
359
- errors.push(*sub_attribute.validate(value, sub_context))
340
+ errors.concat(sub_attribute.validate(value, sub_context))
360
341
  end
361
342
  self.class.attribute.type.requirements.each do |req|
362
343
  validation_errors = req.validate(keys_with_values, context)
363
- errors.push(*validation_errors) unless validation_errors.empty?
344
+ errors.concat(validation_errors) unless validation_errors.empty?
364
345
  end
365
346
  errors
366
347
  ensure
@@ -371,7 +352,5 @@ module Praxis
371
352
  def _get_attr(name)
372
353
  self.send(name)
373
354
  end
374
-
375
355
  end
376
-
377
356
  end