yeti 0.1.1 → 0.1.2

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
@@ -20,7 +20,153 @@ Or install it yourself as:
20
20
 
21
21
  ## Usage
22
22
 
23
- TODO: Write usage instructions here
23
+ ### Yeti::Context
24
+
25
+ The context object allows to communicate application state coming from the
26
+ controllers. First, let's create a Context class tuned to your application
27
+ logic.
28
+
29
+ ```ruby
30
+ class Context < Yeti::Context
31
+ def initialize(hash)
32
+ super hash
33
+ @section = hash.fetch :section
34
+ end
35
+
36
+ def in_admin?
37
+ section==:admin
38
+
39
+ def api_call?
40
+ section==:api
41
+ end
42
+ end
43
+ ```
44
+
45
+ Typical usage in controller:
46
+
47
+ ```ruby
48
+ class Admin::BaseController < ApplicationController
49
+ private
50
+ def current_context
51
+ Context.new account_id: session[:account_id, section: :admin
52
+ end
53
+ end
54
+ class Admin::UsersController < Admin::BaseController
55
+ def index
56
+ @users = UserSearch.new context, params
57
+ end
58
+
59
+ def show
60
+ @user = UserViewer.new context, params[:id]
61
+ end
62
+
63
+ def new
64
+ user
65
+ end
66
+
67
+ def create
68
+ if user.update_attributes params[:user]
69
+ redirect_to action: :index, notice: t(:successfully_created)
70
+ else
71
+ flash.now[:error] = t(:please_fix_errors_and_retry)
72
+ render :new
73
+ end
74
+ end
75
+
76
+ def edit
77
+ user
78
+ end
79
+
80
+ def update
81
+ if user.update_attributes params[:user]
82
+ redirect_to action: :index, notice: t(:successfully_updated)
83
+ else
84
+ flash.now[:error] = t(:please_fix_errors_and_retry)
85
+ render :edit
86
+ end
87
+ end
88
+
89
+ def user
90
+ @user ||= UserEditor.new context, params[:id]
91
+ end
92
+ end
93
+ ```
94
+
95
+ The presenters can then use the context to restrict search results, change
96
+ validation, etc. The context object is also useful to track current account in
97
+ order to apply permissions or know who is performing the action.
98
+
99
+ Let's say you have a Account model:
100
+
101
+ ```ruby
102
+ class Account < ActiveRecord::Base
103
+ def guest?
104
+ false
105
+ end
106
+ end
107
+ ```
108
+
109
+ of course it could be using Sequel, Mongoid or the ORM of your liking. Then you
110
+ can define your context class this way:
111
+
112
+ ```ruby
113
+ class Context < Yeti::Context
114
+ class NoAccount < Yeti::Context::NoAccount
115
+ def guest?
116
+ true
117
+ end
118
+ end
119
+ delegate :guest?, to: :account
120
+
121
+ private
122
+
123
+ def find_account_by_id(id)
124
+ Account.find_by_id id
125
+ end
126
+
127
+ def no_account
128
+ NoAccount.new
129
+ end
130
+ end
131
+ ```
132
+
133
+ And with that setup you get:
134
+
135
+ ```ruby
136
+ existing_account_id = 1
137
+ invalid_id = 2
138
+ Context.new.guest? #=> true
139
+ Context.new(account_id: existing_account_id).guest? #=> false
140
+ Context.new(account_id: invalid_id).guest? #=> true
141
+ ```
142
+
143
+ No more passing tons of variables from presenters to presenters, the context
144
+ object regroups and unify this communication while being easy to test in
145
+ isolation.
146
+
147
+ ### Yeti::Search
148
+
149
+ The search object aims to simplify creating pages with numerous filters.
150
+
151
+ Let's say you have the following Sequel model:
152
+
153
+ ```ruby
154
+ class User < Sequel::Model
155
+ end
156
+ ```
157
+
158
+ then you can create UserSearch like this:
159
+
160
+ ```ruby
161
+ class UserSearch < Yeti::Search
162
+ private
163
+ def paginated_results
164
+ User.search(search).paginate page, per_page
165
+ end
166
+ end
167
+ ```
168
+
169
+ ### Yeti::Editor
24
170
 
25
171
  ## Contributing
26
172
 
data/lib/yeti/context.rb CHANGED
@@ -6,13 +6,15 @@ module Yeti
6
6
 
7
7
  delegate :id, to: :account, prefix: :account
8
8
 
9
- def initialize(hash)
10
- @given_account_id = hash.fetch(:account_id)
9
+ def initialize(hash={})
10
+ @hash = hash
11
11
  end
12
12
 
13
13
  def account
14
- @account ||= find_account_by_id given_account_id if given_account_id
15
- @account ||= no_account
14
+ @account ||= begin
15
+ given_account_id = hash[:account_id]
16
+ (find_account_by_id given_account_id if given_account_id) || no_account
17
+ end
16
18
  end
17
19
 
18
20
  def find_account_by_id(id)
@@ -21,7 +23,7 @@ module Yeti
21
23
 
22
24
  private
23
25
 
24
- attr_reader :given_account_id
26
+ attr_reader :hash
25
27
 
26
28
  def no_account
27
29
  NoAccount.new
data/lib/yeti/editor.rb CHANGED
@@ -6,7 +6,7 @@ module Yeti
6
6
  attr_reader :context
7
7
  delegate :id, to: :edited
8
8
 
9
- def initialize(context, given_id)
9
+ def initialize(context, given_id=nil)
10
10
  @context = context
11
11
  @given_id = given_id
12
12
  end
data/lib/yeti/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Yeti
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.2"
3
3
  end
@@ -1,10 +1,14 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe Yeti::Context do
4
- context "initialization" do
5
- it "requires a hash with account_id" do
6
- lambda{ Yeti::Context.new }.should raise_error ArgumentError, "wrong number of arguments (0 for 1)"
7
- lambda{ Yeti::Context.new key: nil }.should raise_error KeyError, "key not found: :account_id"
4
+ context "without hash" do
5
+ it "#account is an instance of Yeti::Context::NoAccount" do
6
+ Yeti::Context.new.account.should be_kind_of Yeti::Context::NoAccount
7
+ end
8
+ end
9
+ context "when account_id is not in hash" do
10
+ it "#account is an instance of Yeti::Context::NoAccount" do
11
+ Yeti::Context.new({}).account.should be_kind_of Yeti::Context::NoAccount
8
12
  end
9
13
  end
10
14
  context "when account_id is nil" do
@@ -2,7 +2,7 @@ require "spec_helper"
2
2
 
3
3
  describe Yeti::Editor do
4
4
  let(:context){ mock :context }
5
- subject{ Yeti::Editor.new context, nil }
5
+ subject{ Yeti::Editor.new context }
6
6
  it "keeps given context" do
7
7
  subject.context.should be context
8
8
  end
@@ -23,6 +23,14 @@ describe Yeti::Editor do
23
23
  subject.edited.should be expected
24
24
  end
25
25
  end
26
+ context "with id nil" do
27
+ subject{ Yeti::Editor.new context, nil }
28
+ it{ should_not be_persisted }
29
+ it "uses #new_object to initialize main object being edited" do
30
+ subject.stub(:new_object).and_return(expected = mock)
31
+ subject.edited.should be expected
32
+ end
33
+ end
26
34
  context "without id" do
27
35
  it{ should_not be_persisted }
28
36
  it "uses #new_object to initialize main object being edited" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yeti
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-10-20 00:00:00.000000000 Z
12
+ date: 2012-11-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activemodel
@@ -114,7 +114,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
114
114
  version: '0'
115
115
  segments:
116
116
  - 0
117
- hash: -488456883026859265
117
+ hash: 3542103795249747171
118
118
  required_rubygems_version: !ruby/object:Gem::Requirement
119
119
  none: false
120
120
  requirements:
@@ -123,7 +123,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
123
123
  version: '0'
124
124
  segments:
125
125
  - 0
126
- hash: -488456883026859265
126
+ hash: 3542103795249747171
127
127
  requirements: []
128
128
  rubyforge_project:
129
129
  rubygems_version: 1.8.24