yeti 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
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