personhood 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +19 -0
- data/Gemfile.lock +111 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +279 -0
- data/Rakefile +55 -0
- data/VERSION +1 -0
- data/lib/juscribe/display_optional.rb +19 -0
- data/lib/juscribe/name.rb +42 -0
- data/lib/juscribe/personhood.rb +93 -0
- data/lib/juscribe/tos_acceptable.rb +26 -0
- data/lib/personhood.rb +10 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/javascripts/application.js +9 -0
- data/spec/dummy/app/assets/stylesheets/application.css +7 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/.gitkeep +0 -0
- data/spec/dummy/app/models/.gitkeep +0 -0
- data/spec/dummy/app/models/user.rb +3 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/config/application.rb +45 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +30 -0
- data/spec/dummy/config/environments/production.rb +60 -0
- data/spec/dummy/config/environments/test.rb +39 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +10 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +58 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/db/migrate/20121022000000_create_users.rb +19 -0
- data/spec/dummy/db/schema.rb +28 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/lib/assets/.gitkeep +0 -0
- data/spec/dummy/log/.gitkeep +0 -0
- data/spec/dummy/log/development.log +20 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +26 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/personhood_spec.rb +237 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/support/have_error_matchers.rb +46 -0
- metadata +202 -0
data/.document
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/Gemfile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
# Add dependencies required to use your gem here.
|
3
|
+
# Example:
|
4
|
+
# gem "activesupport", ">= 2.3.5"
|
5
|
+
|
6
|
+
# Add dependencies to develop your gem here.
|
7
|
+
# Include everything needed to run rake, tests, features, etc.
|
8
|
+
group :development do
|
9
|
+
# gem "rspec", "~> 2.11.0"
|
10
|
+
gem 'rspec-rails', '~> 2.11'
|
11
|
+
gem "rdoc", "~> 3.12"
|
12
|
+
gem "bundler", ">= 1.0.0"
|
13
|
+
gem "jeweler", "~> 1.8.4"
|
14
|
+
# gem "rcov", ">= 0"
|
15
|
+
|
16
|
+
# gem 'railties', '>= 3.1.0'
|
17
|
+
gem 'rails', '>= 3.1.0'
|
18
|
+
gem 'sqlite3'
|
19
|
+
end
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
actionmailer (3.2.8)
|
5
|
+
actionpack (= 3.2.8)
|
6
|
+
mail (~> 2.4.4)
|
7
|
+
actionpack (3.2.8)
|
8
|
+
activemodel (= 3.2.8)
|
9
|
+
activesupport (= 3.2.8)
|
10
|
+
builder (~> 3.0.0)
|
11
|
+
erubis (~> 2.7.0)
|
12
|
+
journey (~> 1.0.4)
|
13
|
+
rack (~> 1.4.0)
|
14
|
+
rack-cache (~> 1.2)
|
15
|
+
rack-test (~> 0.6.1)
|
16
|
+
sprockets (~> 2.1.3)
|
17
|
+
activemodel (3.2.8)
|
18
|
+
activesupport (= 3.2.8)
|
19
|
+
builder (~> 3.0.0)
|
20
|
+
activerecord (3.2.8)
|
21
|
+
activemodel (= 3.2.8)
|
22
|
+
activesupport (= 3.2.8)
|
23
|
+
arel (~> 3.0.2)
|
24
|
+
tzinfo (~> 0.3.29)
|
25
|
+
activeresource (3.2.8)
|
26
|
+
activemodel (= 3.2.8)
|
27
|
+
activesupport (= 3.2.8)
|
28
|
+
activesupport (3.2.8)
|
29
|
+
i18n (~> 0.6)
|
30
|
+
multi_json (~> 1.0)
|
31
|
+
arel (3.0.2)
|
32
|
+
builder (3.0.4)
|
33
|
+
diff-lcs (1.1.3)
|
34
|
+
erubis (2.7.0)
|
35
|
+
git (1.2.5)
|
36
|
+
hike (1.2.1)
|
37
|
+
i18n (0.6.1)
|
38
|
+
jeweler (1.8.4)
|
39
|
+
bundler (~> 1.0)
|
40
|
+
git (>= 1.2.5)
|
41
|
+
rake
|
42
|
+
rdoc
|
43
|
+
journey (1.0.4)
|
44
|
+
json (1.7.5)
|
45
|
+
mail (2.4.4)
|
46
|
+
i18n (>= 0.4.0)
|
47
|
+
mime-types (~> 1.16)
|
48
|
+
treetop (~> 1.4.8)
|
49
|
+
mime-types (1.19)
|
50
|
+
multi_json (1.3.6)
|
51
|
+
polyglot (0.3.3)
|
52
|
+
rack (1.4.1)
|
53
|
+
rack-cache (1.2)
|
54
|
+
rack (>= 0.4)
|
55
|
+
rack-ssl (1.3.2)
|
56
|
+
rack
|
57
|
+
rack-test (0.6.2)
|
58
|
+
rack (>= 1.0)
|
59
|
+
rails (3.2.8)
|
60
|
+
actionmailer (= 3.2.8)
|
61
|
+
actionpack (= 3.2.8)
|
62
|
+
activerecord (= 3.2.8)
|
63
|
+
activeresource (= 3.2.8)
|
64
|
+
activesupport (= 3.2.8)
|
65
|
+
bundler (~> 1.0)
|
66
|
+
railties (= 3.2.8)
|
67
|
+
railties (3.2.8)
|
68
|
+
actionpack (= 3.2.8)
|
69
|
+
activesupport (= 3.2.8)
|
70
|
+
rack-ssl (~> 1.3.2)
|
71
|
+
rake (>= 0.8.7)
|
72
|
+
rdoc (~> 3.4)
|
73
|
+
thor (>= 0.14.6, < 2.0)
|
74
|
+
rake (0.9.2.2)
|
75
|
+
rdoc (3.12)
|
76
|
+
json (~> 1.4)
|
77
|
+
rspec (2.11.0)
|
78
|
+
rspec-core (~> 2.11.0)
|
79
|
+
rspec-expectations (~> 2.11.0)
|
80
|
+
rspec-mocks (~> 2.11.0)
|
81
|
+
rspec-core (2.11.1)
|
82
|
+
rspec-expectations (2.11.3)
|
83
|
+
diff-lcs (~> 1.1.3)
|
84
|
+
rspec-mocks (2.11.3)
|
85
|
+
rspec-rails (2.11.4)
|
86
|
+
actionpack (>= 3.0)
|
87
|
+
activesupport (>= 3.0)
|
88
|
+
railties (>= 3.0)
|
89
|
+
rspec (~> 2.11.0)
|
90
|
+
sprockets (2.1.3)
|
91
|
+
hike (~> 1.2)
|
92
|
+
rack (~> 1.0)
|
93
|
+
tilt (~> 1.1, != 1.3.0)
|
94
|
+
sqlite3 (1.3.6)
|
95
|
+
thor (0.16.0)
|
96
|
+
tilt (1.3.3)
|
97
|
+
treetop (1.4.11)
|
98
|
+
polyglot
|
99
|
+
polyglot (>= 0.3.1)
|
100
|
+
tzinfo (0.3.33)
|
101
|
+
|
102
|
+
PLATFORMS
|
103
|
+
ruby
|
104
|
+
|
105
|
+
DEPENDENCIES
|
106
|
+
bundler (>= 1.0.0)
|
107
|
+
jeweler (~> 1.8.4)
|
108
|
+
rails (>= 3.1.0)
|
109
|
+
rdoc (~> 3.12)
|
110
|
+
rspec-rails (~> 2.11)
|
111
|
+
sqlite3
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 caleon
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,279 @@
|
|
1
|
+
= Personhood
|
2
|
+
|
3
|
+
When you are tired of coding the same kinds of things for your User model (or any other person-like model) with all its typical +first_name+, +full_name+, and other brouhaha, use the Personhood gem to clean up your code by getting rid of those pesky lines and instead focusing on the lines of code that *truly* set your app apart.
|
4
|
+
|
5
|
+
This is a bit of an opinionated library, so the following assumptions are in place (although in the future this can be made more customizable):
|
6
|
+
|
7
|
+
== Assumptions
|
8
|
+
|
9
|
+
1. Your class is expected to have the following attributes:
|
10
|
+
|
11
|
+
* email (string)
|
12
|
+
* username (string)
|
13
|
+
* first_name (string)
|
14
|
+
* middle_name (string)
|
15
|
+
* last_name (string)
|
16
|
+
* birthdate (date)
|
17
|
+
* sex (integer)
|
18
|
+
|
19
|
+
2. All attributes listed above are +attr_accessible+ as +:admin+.
|
20
|
+
|
21
|
+
3. All but the first two attributes (email/username) listed above are +attr_accessible+ under normal cases.
|
22
|
+
|
23
|
+
4. Out of the box, +last_name+ and +first_name+ are required. They, along with +middle_name+ are allowed at most 255 characters.
|
24
|
+
|
25
|
+
5. As a +before_save+ callback, whitespaces are stripped from each of +last_name+, +first_name+, and +middle_name+.
|
26
|
+
|
27
|
+
6. A record without a +birthdate+ attribute set (or nil) is treated as one whose birthdate is today, hence +age+ would be 0.
|
28
|
+
|
29
|
+
== Customizations
|
30
|
+
|
31
|
+
Currently the only customization at the API level is with regard to overriding the +#to_param+ instance method. Under normal circumstances you would override this to be used as a URL slug (permalinking with strings instead of ID numerics), but the library goes further to set the return value of +#to_s+ to the value of +#to_param+.
|
32
|
+
|
33
|
+
Typically, I would suggest you defining your custom +#to_param+ within your model, but if you need to customize +#to_param+ for your own needs but do not wish to have its value associated with +#to_s+, you can override that method instead.
|
34
|
+
|
35
|
+
== Usage
|
36
|
+
|
37
|
+
=== Installation
|
38
|
+
|
39
|
+
Gemfile:
|
40
|
+
|
41
|
+
gem 'personhood', github: 'caleon/personhood'
|
42
|
+
|
43
|
+
Model class file (i.e. +app/models/user.rb+):
|
44
|
+
|
45
|
+
class User
|
46
|
+
include Juscribe::Personhood
|
47
|
+
# ..
|
48
|
+
end
|
49
|
+
|
50
|
+
The include statement does not necessarily have to be at the very top of the class definition, but just be aware that upon inclusion, the module also writes a +composed_of+ statement, +validates+ defaults, and the aforementioned +attr_accessible+ rules.
|
51
|
+
|
52
|
+
=== API
|
53
|
+
|
54
|
+
Once your model is properly set up, these are the methods available to you:
|
55
|
+
|
56
|
+
# Given:
|
57
|
+
user = User.new({
|
58
|
+
email: 'john@doe.com',
|
59
|
+
username: 'johnDoe'
|
60
|
+
first_name: 'john',
|
61
|
+
last_name: 'doe',
|
62
|
+
middle_name: 'quincy',
|
63
|
+
sex: 1,
|
64
|
+
birthdate: Date.new(1970, 1, 1)
|
65
|
+
}, as: :admin)
|
66
|
+
|
67
|
+
Getting back on track, here are the _basic_ methods:
|
68
|
+
|
69
|
+
user.to_param
|
70
|
+
# => "johnDoe"
|
71
|
+
|
72
|
+
user.to_s
|
73
|
+
# => "johnDoe"
|
74
|
+
|
75
|
+
user.full_name
|
76
|
+
# => "john q. doe"
|
77
|
+
|
78
|
+
user.first_and_last_name
|
79
|
+
# => "john doe"
|
80
|
+
|
81
|
+
user.email_address
|
82
|
+
# => "john doe <john@doe.com>"
|
83
|
+
|
84
|
+
user.sex
|
85
|
+
# => "m"
|
86
|
+
|
87
|
+
user.sex(:full)
|
88
|
+
# => "male"
|
89
|
+
|
90
|
+
user.male?
|
91
|
+
# => true
|
92
|
+
|
93
|
+
user.female?
|
94
|
+
# => false
|
95
|
+
|
96
|
+
user.androgynous?
|
97
|
+
# => false
|
98
|
+
|
99
|
+
# Indeterminate sex:
|
100
|
+
andro = User.new
|
101
|
+
|
102
|
+
andro.sex
|
103
|
+
# => "?"
|
104
|
+
|
105
|
+
andro.sex(:full)
|
106
|
+
# => "(unknown)"
|
107
|
+
|
108
|
+
andro.androgynous?
|
109
|
+
# => true
|
110
|
+
|
111
|
+
user.age
|
112
|
+
# returns whatever the age happens to be for someone born on 1990/1/1
|
113
|
+
|
114
|
+
andro.age
|
115
|
+
# => 0
|
116
|
+
|
117
|
+
The following are available due to the underlying Juscribe::Name method. You are free to interact with the Juscribe::Name class if you'd like, but the intent is to be hidden from your code and interacted only via the Juscribe::Personhood module which gets included in your class.
|
118
|
+
|
119
|
+
user.name
|
120
|
+
# => "john quincy doe"
|
121
|
+
|
122
|
+
user.name.class
|
123
|
+
# => Juscribe::Name
|
124
|
+
|
125
|
+
user.name.to_s
|
126
|
+
# => "john q. doe"
|
127
|
+
|
128
|
+
user.name.first
|
129
|
+
# => "john"
|
130
|
+
|
131
|
+
user.name.middle
|
132
|
+
# => "quincy"
|
133
|
+
|
134
|
+
user.name.middle_initial
|
135
|
+
# => "q."
|
136
|
+
|
137
|
+
user.name.last
|
138
|
+
# => "doe"
|
139
|
+
|
140
|
+
user.name.full
|
141
|
+
# => "john q. doe"
|
142
|
+
|
143
|
+
user.name.complete
|
144
|
+
# => "john quincy doe"
|
145
|
+
|
146
|
+
The other byproduct of using this intermediary class is that it allows usage of +ActiveRecord+'s +composed_of+ statement:
|
147
|
+
|
148
|
+
user = User.create(name: 'Jane Zeta Doe') do |u|
|
149
|
+
# ..
|
150
|
+
# Assume other required attributes are set here
|
151
|
+
# ..
|
152
|
+
end
|
153
|
+
|
154
|
+
user.first_name
|
155
|
+
# => "Jane"
|
156
|
+
|
157
|
+
user.middle_name
|
158
|
+
# => "Zeta"
|
159
|
+
|
160
|
+
user.middle_initial
|
161
|
+
# => "Z."
|
162
|
+
|
163
|
+
user.last_name
|
164
|
+
# => "Doe"
|
165
|
+
|
166
|
+
name = Juscribe::Name.new('Jane', 'Zeta', 'Doe')
|
167
|
+
# or
|
168
|
+
name = Juscribe::Name.convert('Jane Zeta Doe')
|
169
|
+
|
170
|
+
User.where(name: name)
|
171
|
+
# User Load (0.6ms) SELECT `users`.* FROM `users` WHERE `users`.`first_name` = 'Jane' AND `users`.`middle_name` = 'Zeta' AND `users`.`last_name` = 'Doe'
|
172
|
+
# => [#<User id: ..>]
|
173
|
+
|
174
|
+
# Note: this means all mapped name parts must match exactly:
|
175
|
+
name = Juscribe::Name.convert('Jane Doe')
|
176
|
+
User.where(name: name)
|
177
|
+
# User Load (0.6ms) SELECT `users`.* FROM `users` WHERE `users`.`first_name` = 'Jane' AND `users`.`middle_name` IS NULL AND `users`.`last_name` = 'Doe'
|
178
|
+
# => []
|
179
|
+
|
180
|
+
If you see yourself using Juscribe::Name a lot, you might want to consider aliasing it at the root level with:
|
181
|
+
|
182
|
+
Name = Juscribe::Name
|
183
|
+
|
184
|
+
=== Sidenote
|
185
|
+
|
186
|
+
Examples above were only using the +:admin+ role for demonstration. Ideally such account-level attributes should undergo more strenuous security checks in your controller. A simple way would be:
|
187
|
+
|
188
|
+
user = User.new(params[:user]) do |u|
|
189
|
+
u.email = params[:user][:email]
|
190
|
+
u.username = params[:user][:username]
|
191
|
+
end
|
192
|
+
|
193
|
+
Values for +#sex+ are left lowercased because I'd argue capitalization should be handled at the CSS level with something like:
|
194
|
+
|
195
|
+
.user .sex { text-transform: capitalize; }
|
196
|
+
|
197
|
+
=== Other modules included as a dependency
|
198
|
+
|
199
|
+
==== Juscribe::Name
|
200
|
+
|
201
|
+
The usage of this class is demonstrated above and its purpose is for abstraction and integration with +composed_of+.
|
202
|
+
|
203
|
+
==== Juscribe::DisplayOptional
|
204
|
+
|
205
|
+
This is used in the inner-workings of +Personhood+ to display a default "unknown" string if the actual value is blank.
|
206
|
+
|
207
|
+
==== Juscribe::TosAcceptable
|
208
|
+
|
209
|
+
This module is included in the gem now, but I'm considering taking it out, as it applies less to the "personhood" of an object but more to its "user-account-ness". For now, you may go on to use it in the following way:
|
210
|
+
|
211
|
+
# within app/models/user.rb
|
212
|
+
include Juscribe::TosAcceptable
|
213
|
+
|
214
|
+
# within migration
|
215
|
+
add_column :users, :tos_accepted_at, :datetime
|
216
|
+
|
217
|
+
# within your registration form
|
218
|
+
<%= form_for User.new do |f| %>
|
219
|
+
<%#= .. other form stuff %>
|
220
|
+
|
221
|
+
<%= f.label :tos_accepted do %>
|
222
|
+
<%= f.check_box :tos_accepted, value: '1' %>
|
223
|
+
I agree to the <%= link_to 'Terms of Use', terms_path %>.
|
224
|
+
<% end %>
|
225
|
+
|
226
|
+
<%= f.submit %>
|
227
|
+
<% end %>
|
228
|
+
|
229
|
+
In the background, there is a faux-attribute-accessor for +:tos_accepted+ which, when asked to write a truthy form value, writes the timestamp when the form was submitted.
|
230
|
+
|
231
|
+
Note that by including the Juscribe::TosAcceptable you are also adding the acceptance validation on that field (existing records without that field set will not validate).
|
232
|
+
|
233
|
+
== Roadmap
|
234
|
+
|
235
|
+
=== rex_validate
|
236
|
+
|
237
|
+
If you looked in the source code you will find references to regexp validation commented out. As this gem was lifted from my other projects which typically use the rex_validate gem, I had to take out that aspect of the integration for the time being while I work out a cleaner way to integrate the two.
|
238
|
+
|
239
|
+
=== More convenient Query parameters
|
240
|
+
|
241
|
+
As in: "User.where(name: 'John Doe')"" instead of instantiating a new object of the Juscribe::Name class.
|
242
|
+
|
243
|
+
=== Customizable column names
|
244
|
+
|
245
|
+
Shouldn't be hard to do, and the aim is to not do +include Juscribe::Personhood+ and instead do:
|
246
|
+
|
247
|
+
class User < ActiveRecord::Base
|
248
|
+
claims_personhood columns: {
|
249
|
+
first_name: :name1,
|
250
|
+
# ..
|
251
|
+
birthdate: :dob
|
252
|
+
},
|
253
|
+
defaults: {
|
254
|
+
birthdate: Date.new(1900, 1, 1),
|
255
|
+
age: nil
|
256
|
+
}
|
257
|
+
end
|
258
|
+
|
259
|
+
... although it might be simpler to +alias_attribute+ and override the +birthdate+ or +age+ methods in those options.
|
260
|
+
|
261
|
+
=== General Direction
|
262
|
+
|
263
|
+
While it might occur to some to include as options basic groups of functionality involving things like a person's address or profile information, the initial intent of the Personhood library was to contain just the bare minimum commonalities among typical projects. Furthermore, it should apply to non-user-related classes like +President+, for instance, which contains in its table a listing of all the presidents. In such a case, things like even +email_address+ are irrelevant and should not really apply. More fitting, perhaps, are extensions involving things like a person's biometrics (eye color, height, weight), birthplace or ethnicity.
|
264
|
+
|
265
|
+
== Contributing to Personhood
|
266
|
+
|
267
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
268
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
269
|
+
* Fork the project.
|
270
|
+
* Start a feature/bugfix branch.
|
271
|
+
* Commit and push until you are happy with your contribution.
|
272
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
273
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
274
|
+
|
275
|
+
== Copyright
|
276
|
+
|
277
|
+
Copyright (c) 2012 caleon. See LICENSE.txt for
|
278
|
+
further details.
|
279
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "personhood"
|
18
|
+
gem.homepage = "http://github.com/caleon/personhood"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = %Q{ActiveRecord abstraction for attributes pertaining to a person-like model}
|
21
|
+
gem.description = <<-DESC
|
22
|
+
When you are tired of coding the same kinds of things for your User model
|
23
|
+
(or any other person-like model) with all its typical `first_name`,
|
24
|
+
`full_name`, and other brouhaha, use the Personhood gem to clean up your
|
25
|
+
code by getting rid of those pesky lines and instead focusing on the lines of
|
26
|
+
code that *truly* set your app apart.
|
27
|
+
DESC
|
28
|
+
gem.email = "caleon@gmail.com"
|
29
|
+
gem.authors = ["caleon"]
|
30
|
+
# dependencies defined in Gemfile
|
31
|
+
end
|
32
|
+
Jeweler::RubygemsDotOrgTasks.new
|
33
|
+
|
34
|
+
require 'rspec/core'
|
35
|
+
require 'rspec/core/rake_task'
|
36
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
37
|
+
spec.pattern = FileList['spec/**/*_spec.rb']
|
38
|
+
end
|
39
|
+
|
40
|
+
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
41
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
42
|
+
spec.rcov = true
|
43
|
+
end
|
44
|
+
|
45
|
+
task :default => :spec
|
46
|
+
|
47
|
+
require 'rdoc/task'
|
48
|
+
Rake::RDocTask.new do |rdoc|
|
49
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
50
|
+
|
51
|
+
rdoc.rdoc_dir = 'rdoc'
|
52
|
+
rdoc.title = "Personhood #{version}"
|
53
|
+
rdoc.rdoc_files.include('README*')
|
54
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
55
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.2
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Juscribe # :nodoc:
|
2
|
+
module DisplayOptional
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
class_attribute :unknown_label
|
7
|
+
self.unknown_label = 'unnamed'
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
display_optional(to_param)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
def display_optional(value)
|
16
|
+
value.blank? ? "(#{unknown_label})" : value
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Juscribe
|
2
|
+
class Name
|
3
|
+
attr_reader :first, :middle, :last
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def convert(input)
|
7
|
+
case input
|
8
|
+
when String
|
9
|
+
new(*input.split(/\s+/))
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(first, *others, last)
|
15
|
+
@first, @middle, @last = first, others.first, last
|
16
|
+
end
|
17
|
+
|
18
|
+
def middle_initial
|
19
|
+
"#{middle.first}." if middle.present?
|
20
|
+
end
|
21
|
+
|
22
|
+
def first_and_last
|
23
|
+
[first, *last].join(' ')
|
24
|
+
end
|
25
|
+
|
26
|
+
def full
|
27
|
+
[first, *middle_initial, *last].join(' ')
|
28
|
+
end
|
29
|
+
|
30
|
+
def complete
|
31
|
+
[first, *middle, *last].join(' ')
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_s
|
35
|
+
full
|
36
|
+
end
|
37
|
+
|
38
|
+
def inspect
|
39
|
+
%Q{"#{complete}"}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Juscribe # :nodoc:
|
2
|
+
module Personhood
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
composed_of :name, class_name: 'Juscribe::Name',
|
7
|
+
converter: :convert,
|
8
|
+
mapping: [%w(first_name first),
|
9
|
+
%w(middle_name middle),
|
10
|
+
%w(last_name last)]
|
11
|
+
|
12
|
+
# rex_validate :last_name, :first_name, :middle_name
|
13
|
+
with_options length: { maximum: 255 } do |x|
|
14
|
+
x.validates :last_name, :first_name, presence: true
|
15
|
+
x.validates :middle_name
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_accessible :last_name, :first_name, :middle_name, :name, :birthdate, :sex
|
19
|
+
attr_accessible :last_name, :first_name, :middle_name, :name, :birthdate, :sex, :email, :username, as: :admin
|
20
|
+
end
|
21
|
+
|
22
|
+
# ----------------------------------------
|
23
|
+
# :section: Basic Info
|
24
|
+
# ----------------------------------------
|
25
|
+
|
26
|
+
# Override the following to control what gets displayed on #to_s
|
27
|
+
def to_param
|
28
|
+
username
|
29
|
+
end
|
30
|
+
|
31
|
+
def middle_initial
|
32
|
+
display_optional name.middle_initial
|
33
|
+
end
|
34
|
+
|
35
|
+
##
|
36
|
+
# Full name, unless empty, then shows "unnamed" variant.
|
37
|
+
def full_name
|
38
|
+
display_optional name.full
|
39
|
+
end
|
40
|
+
|
41
|
+
# Same as #full_name but omitting the middle_name.
|
42
|
+
def first_and_last_name
|
43
|
+
display_optional name.first_and_last
|
44
|
+
end
|
45
|
+
|
46
|
+
# Email, when the db field is nil, still returns an empty string,
|
47
|
+
# I suppose for Devise's sake.
|
48
|
+
def email_address
|
49
|
+
"#{first_and_last_name} <#{email}>" if email.present?
|
50
|
+
end
|
51
|
+
|
52
|
+
# Defaults to <tt>Date.today</tt> if +nil+.
|
53
|
+
def birthdate
|
54
|
+
read_attribute(:birthdate) || Date.today
|
55
|
+
end
|
56
|
+
|
57
|
+
def age
|
58
|
+
today, bday = Date.today, birthdate
|
59
|
+
years = today.year - bday.year
|
60
|
+
years.tap { |yrs| yrs -= 1 if (bday + years.years) > today }
|
61
|
+
# is this logic even solid?
|
62
|
+
end
|
63
|
+
|
64
|
+
def sex(format = nil)
|
65
|
+
full_int = format == :full ? 1 : 0
|
66
|
+
if read_attribute(:sex)
|
67
|
+
%w(female male)[self[:sex]][0..full_int.send(:-@)]
|
68
|
+
else
|
69
|
+
%w{? (unknown)}[full_int]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def male?
|
74
|
+
sex == 'm'
|
75
|
+
end
|
76
|
+
|
77
|
+
def female?
|
78
|
+
sex == 'f'
|
79
|
+
end
|
80
|
+
|
81
|
+
def androgynous?
|
82
|
+
read_attribute(:sex).nil?
|
83
|
+
end
|
84
|
+
|
85
|
+
private
|
86
|
+
def strip_names!
|
87
|
+
%w(first_name middle_name last_name).each do |name_part|
|
88
|
+
send(name_part).strip! if send(:"#{name_part}_changed?")
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Juscribe # :nodoc:
|
2
|
+
module TosAcceptable
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
validates :terms_of_service, acceptance: { accept: true }
|
7
|
+
|
8
|
+
attr_accessor :tos_accepted, :tos_accepted_at
|
9
|
+
end
|
10
|
+
|
11
|
+
def tos_accepted=(val)
|
12
|
+
self.tos_accepted_at = Time.now
|
13
|
+
end
|
14
|
+
|
15
|
+
def tos_accepted_at=(timestamp)
|
16
|
+
write_attribute(:tos_accepted_at, timestamp) if has_attribute?(:tos_accepted_at)
|
17
|
+
@_tos_accepted_at = timestamp
|
18
|
+
end
|
19
|
+
|
20
|
+
def tos_accepted?
|
21
|
+
@_tos_accepted_at.try(:<, Time.now)
|
22
|
+
end
|
23
|
+
alias_method :tos_accepted, :tos_accepted?
|
24
|
+
alias_method :terms_of_service, :tos_accepted?
|
25
|
+
end
|
26
|
+
end
|
data/lib/personhood.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'active_support/concern'
|
3
|
+
require 'active_support/core_ext/object/blank'
|
4
|
+
require 'active_support/core_ext/class/attribute'
|
5
|
+
require 'juscribe/display_optional'
|
6
|
+
require 'juscribe/name'
|
7
|
+
require 'juscribe/personhood'
|
8
|
+
require 'juscribe/tos_acceptable'
|
9
|
+
|
10
|
+
ActiveRecord::Base.send(:include, Juscribe::DisplayOptional)
|