decent_exposure 2.0.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md CHANGED
@@ -284,7 +284,7 @@ This block is evaluated and the memoized result is returned whenever you call
284
284
 
285
285
  For the times when custom behavior is needed for resource finding,
286
286
  `decent_exposure` provides a base class for extending. For example, if
287
- scoping a resource from `current_user` is not and option, but you'd like
287
+ scoping a resource from `current_user` is not an option, but you'd like
288
288
  to verify a resource's relationship to the `current_user`, you can use a
289
289
  custom strategy like the following:
290
290
 
@@ -353,4 +353,52 @@ And opt into it like so:
353
353
  expose(:article, config: :sluggable)
354
354
  ```
355
355
 
356
- [1]: http://blog.voxdolo.me/a-diatribe-on-maintaining-state.html
356
+ ## Testing
357
+
358
+ Controller testing remains trivially easy. The shift is that you now set expectations on methods rather than instance variables. With RSpec, this mostly means avoiding `assign` and `assigns`.
359
+
360
+ ```ruby
361
+ describe CompaniesController do
362
+ describe "GET index" do
363
+
364
+ # this...
365
+ it "assigns @companies" do
366
+ company = Company.create
367
+ get :index
368
+ assigns(:companies).should eq([company])
369
+ end
370
+
371
+ # becomes this
372
+ it "exposes companies" do
373
+ company = Company.create
374
+ get :index
375
+ controller.companies.should eq([company])
376
+ end
377
+ end
378
+ end
379
+ ```
380
+
381
+ View specs follow a similar pattern:
382
+
383
+ ```ruby
384
+ describe "people/index.html.erb" do
385
+
386
+ # this...
387
+ it "lists people" do
388
+ assign(:people, [ mock_model(Person, name: 'John Doe') ])
389
+ render
390
+ rendered.should have_content('John Doe')
391
+ end
392
+
393
+ # becomes this
394
+ it "lists people" do
395
+ view.stub(people: [ mock_model(Person, name: 'John Doe') ])
396
+ render
397
+ rendered.should have_content('John Doe')
398
+ end
399
+
400
+ end
401
+ ```
402
+
403
+
404
+ [1]: http://blog.voxdolo.me/a-diatribe-on-maintaining-state.html
@@ -2,19 +2,20 @@ require 'decent_exposure/active_record_strategy'
2
2
 
3
3
  module DecentExposure
4
4
  class ActiveRecordWithEagerAttributesStrategy < ActiveRecordStrategy
5
- delegate :get?, :to => :request
5
+ delegate :get?, :to => :request
6
+ delegate :delete?, :to => :request
6
7
 
7
8
  def singular?
8
9
  !plural?
9
10
  end
10
11
 
11
12
  def attributes
12
- params[inflector.singular]
13
+ params[inflector.singular] || {}
13
14
  end
14
15
 
15
16
  def assign_attributes?
16
17
  return false unless attributes && singular?
17
- !get? || new_record?
18
+ (!get? && !delete?) || new_record?
18
19
  end
19
20
 
20
21
  def new_record?
@@ -7,7 +7,7 @@ module DecentExposure
7
7
  def initialize(name, strategy, options)
8
8
  self.strategy = strategy
9
9
  self.options = options
10
- self.inflector = DecentExposure::Inflector.new(name)
10
+ self.inflector = DecentExposure::Inflector.new(name, options[:model])
11
11
  end
12
12
 
13
13
  def call(controller)
@@ -1,22 +1,23 @@
1
1
  require 'active_support/inflector'
2
- require 'active_support/core_ext/string/inflections'
2
+ require 'active_support/core_ext/string'
3
3
 
4
4
  module DecentExposure
5
5
  class Inflector
6
- attr_reader :string, :original
6
+ attr_reader :string, :original, :model
7
7
  alias name string
8
8
 
9
- def initialize(name)
10
- @original = name
11
- @string = name.to_s.demodulize
9
+ def initialize(name, model=nil)
10
+ @original = name.to_s
11
+ @model = model
12
+ @string = (model || name).to_s.demodulize
12
13
  end
13
14
 
14
15
  def constant(context=Object)
15
- case original
16
+ case model
16
17
  when Module, Class
17
- original
18
+ model
18
19
  else
19
- context.const_get string.classify
20
+ ConstantResolver.new(context, string.classify).constant
20
21
  end
21
22
  end
22
23
 
@@ -25,7 +26,7 @@ module DecentExposure
25
26
  end
26
27
 
27
28
  def singular
28
- string.parameterize
29
+ @singular ||= string.singularize.parameterize
29
30
  end
30
31
 
31
32
  def plural
@@ -34,7 +35,37 @@ module DecentExposure
34
35
  alias collection plural
35
36
 
36
37
  def plural?
37
- plural == string
38
+ original.pluralize == original && !uncountable?
39
+ end
40
+
41
+ def uncountable?
42
+ original.pluralize == original.singularize
43
+ end
44
+
45
+ private
46
+
47
+ ConstantResolver = Struct.new :context, :constant_name do
48
+
49
+ def constant
50
+ immediate_child || namespace_qualified
51
+ end
52
+
53
+ private
54
+
55
+ def immediate_child
56
+ context.constants.map do |c|
57
+ context.const_get(c) if c.to_s == constant_name
58
+ end.compact.first
59
+ end
60
+
61
+ def namespace_qualified
62
+ namespace.const_get(constant_name)
63
+ end
64
+
65
+ def namespace
66
+ path = context.to_s
67
+ path[0...(path.rindex('::') || 0)].constantize
68
+ end
38
69
  end
39
70
  end
40
71
  end
@@ -1,3 +1,3 @@
1
1
  module DecentExposure #:nodoc
2
- VERSION = "2.0.0"
2
+ VERSION = "2.0.1"
3
3
  end
metadata CHANGED
@@ -1,8 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: decent_exposure
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.0
5
4
  prerelease:
5
+ version: 2.0.1
6
6
  platform: ruby
7
7
  authors:
8
8
  - Stephen Caudill
@@ -11,72 +11,72 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2012-10-10 00:00:00.000000000 Z
14
+ date: 2012-12-14 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
- name: rspec
18
- requirement: !ruby/object:Gem::Requirement
19
- none: false
17
+ version_requirements: !ruby/object:Gem::Requirement
20
18
  requirements:
21
19
  - - ~>
22
20
  - !ruby/object:Gem::Version
23
21
  version: '2.7'
22
+ none: false
23
+ name: rspec
24
24
  type: :development
25
25
  prerelease: false
26
- version_requirements: !ruby/object:Gem::Requirement
27
- none: false
26
+ requirement: !ruby/object:Gem::Requirement
28
27
  requirements:
29
28
  - - ~>
30
29
  - !ruby/object:Gem::Version
31
30
  version: '2.7'
32
- - !ruby/object:Gem::Dependency
33
- name: rspec-rails
34
- requirement: !ruby/object:Gem::Requirement
35
31
  none: false
32
+ - !ruby/object:Gem::Dependency
33
+ version_requirements: !ruby/object:Gem::Requirement
36
34
  requirements:
37
35
  - - ~>
38
36
  - !ruby/object:Gem::Version
39
37
  version: '2.7'
38
+ none: false
39
+ name: rspec-rails
40
40
  type: :development
41
41
  prerelease: false
42
- version_requirements: !ruby/object:Gem::Requirement
43
- none: false
42
+ requirement: !ruby/object:Gem::Requirement
44
43
  requirements:
45
44
  - - ~>
46
45
  - !ruby/object:Gem::Version
47
46
  version: '2.7'
48
- - !ruby/object:Gem::Dependency
49
- name: actionpack
50
- requirement: !ruby/object:Gem::Requirement
51
47
  none: false
48
+ - !ruby/object:Gem::Dependency
49
+ version_requirements: !ruby/object:Gem::Requirement
52
50
  requirements:
53
51
  - - ~>
54
52
  - !ruby/object:Gem::Version
55
53
  version: '3.1'
54
+ none: false
55
+ name: actionpack
56
56
  type: :development
57
57
  prerelease: false
58
- version_requirements: !ruby/object:Gem::Requirement
59
- none: false
58
+ requirement: !ruby/object:Gem::Requirement
60
59
  requirements:
61
60
  - - ~>
62
61
  - !ruby/object:Gem::Version
63
62
  version: '3.1'
64
- - !ruby/object:Gem::Dependency
65
- name: activesupport
66
- requirement: !ruby/object:Gem::Requirement
67
63
  none: false
64
+ - !ruby/object:Gem::Dependency
65
+ version_requirements: !ruby/object:Gem::Requirement
68
66
  requirements:
69
67
  - - ~>
70
68
  - !ruby/object:Gem::Version
71
69
  version: '3.1'
70
+ none: false
71
+ name: activesupport
72
72
  type: :development
73
73
  prerelease: false
74
- version_requirements: !ruby/object:Gem::Requirement
75
- none: false
74
+ requirement: !ruby/object:Gem::Requirement
76
75
  requirements:
77
76
  - - ~>
78
77
  - !ruby/object:Gem::Version
79
78
  version: '3.1'
79
+ none: false
80
80
  description: ! "\n DecentExposure helps you program to an interface, rather than
81
81
  an\n implementation in your Rails controllers. The fact of the matter is that\n
82
82
  \ sharing state via instance variables in controllers promotes close coupling\n
@@ -107,17 +107,17 @@ rdoc_options:
107
107
  require_paths:
108
108
  - lib
109
109
  required_ruby_version: !ruby/object:Gem::Requirement
110
- none: false
111
110
  requirements:
112
111
  - - ! '>='
113
112
  - !ruby/object:Gem::Version
114
113
  version: '0'
115
- required_rubygems_version: !ruby/object:Gem::Requirement
116
114
  none: false
115
+ required_rubygems_version: !ruby/object:Gem::Requirement
117
116
  requirements:
118
117
  - - ! '>='
119
118
  - !ruby/object:Gem::Version
120
119
  version: 1.3.6
120
+ none: false
121
121
  requirements: []
122
122
  rubyforge_project:
123
123
  rubygems_version: 1.8.24