Skip to content

Instantly share code, notes, and snippets.

@SkylerLipthay
Last active February 1, 2016 07:18
Show Gist options
  • Save SkylerLipthay/668b80a2177955681dc6 to your computer and use it in GitHub Desktop.
Save SkylerLipthay/668b80a2177955681dc6 to your computer and use it in GitHub Desktop.
orm_model! {
table = "admins";
fields = {
*id: i64,
company_id: i64,
name: String,
email: String,
password: Password,
password_reset_token: Option<String>,
created_at: DateTime,
updated_at: DateTime
};
extensions = {
// Here, `AdminName`, `AdminEmail`, etc. are generated by the `orm_model!` macro by
// camel-casing the `fields` given above.
ValidateLength<AdminName, String> = ValidateLength::new(2..256),
ValidateLength<AdminEmail, String> = ValidateLength::new(3..256),
ValidatePresence<AdminPassword, Password> = ValidatePresence::new(),
ValidateUniqueness<AdminEmail, String> = ValidateUniqueness::new(),
Timestamps<AdminCreatedAt, AdminUpdatedAt> = Timestamps::new(),
};
pub struct Admin;
}
impl Admin {
pub fn generate_reset_password_token(&mut self) {
self.set_password_reset_token(Some(thread_rng().gen_ascii_chars().take(32).collect()));
}
}
pub struct UnpackAdmin;
impl Deserialize<Admin> for UnpackAdmin {
fn deserialize(map: &::params::Map, admin: &mut Admin) -> Result<(), Error> {
try!(AdminName::deserialize_permit(map, admin));
try!(AdminEmail::deserialize_permit(map, admin));
try!(AdminPassword::deserialize_permit(map, admin));
Ok(())
}
}
pub struct UnpackLogin;
impl Deserialize<Admin> for UnpackLogin {
fn deserialize(map: &::params::Map, admin: &mut Admin) -> Result<(), Error> {
try!(AdminEmail::deserialize_require(map, admin));
try!(AdminPassword::deserialize_require(map, admin));
Ok(())
}
}
// let admin = try!(Admin::deserialize::<UnpackLogin>(&request_params.get("login_credentials")));
/// Validates the length (in unicode characters) of a string is within a range.
pub struct ValidateLength<M, F, S>
where M: Model, F: ModelField<M, S>, S: AsOption<String> + Value
{
m: PhantomData<fn(M)>,
f: PhantomData<F>,
s: PhantomData<S>,
min: usize,
max: usize,
}
impl<M, F, S> ValidateLength<M, F, S>
where M: Model, F: ModelField<M, S>, S: AsOption<String> + Value
{
pub fn new<R: RangeArgument<usize>>(range: R) -> ValidateLength<M, F, S> {
ValidateLength {
m: PhantomData,
f: PhantomData,
s: PhantomData,
min: range.start().map(|v| *v).unwrap_or(0),
max: range.end().map(|v| *v).unwrap_or(::std::usize::MAX),
}
}
}
impl<M, F, S> Extension<M> for ValidateLength<M, F, S>
where M: Model, F: ModelField<M, S>, S: AsOption<String> + Value
{
fn validate(&self, model: &mut M, tree: &mut ValidationTree) -> Result<(), Error> {
let field = F::field(model);
if !field.has_changed() {
return Ok(());
}
let value = match field.get_opt().map(|v| v.as_option()) {
Some(Some(value)) => value,
None if model.is_new_record() && self.min > 0 => {
tree.add_with_field::<F, _>("cannot be blank".into());
return Ok(());
},
_ => return Ok(()),
};
let length = value.chars().count();
if length == 0 && self.min > 0 {
tree.add_with_field::<F, _>("cannot be blank".into());
} else if length < self.min {
let error = format!("must be at least {} characters", self.min);
tree.add_with_field::<F, _>(error.into());
} else if length > self.max {
let error = format!("cannot be over {} characters", self.max);
tree.add_with_field::<F, _>(error.into());
}
Ok(())
}
}
pub struct ValidateUniqueness<M: Model, F: ModelField<M, V>, V: Value> {
m: PhantomData<fn(M)>,
f: PhantomData<F>,
v: PhantomData<V>,
}
impl<M: Model, F: ModelField<M, V>, V: Value> ValidateUniqueness<M, F, V> {
pub fn new() -> ValidateUniqueness<M, F, V> {
ValidateUniqueness { m: PhantomData, f: PhantomData, v: PhantomData }
}
}
impl<M: Model, F: ModelField<M, V>, V: Value> Extension<M> for ValidateUniqueness<M, F, V> {
fn validate_db<C: Connection>(&self, model: &mut M, tree: &mut ValidationTree, conn: &C)
-> Result<(), Error>
{
if !F::field(model).has_changed() {
return Ok(());
}
// TODO: Scoped finder with `ValidateUniqueness::scoped(|finder| ...)`
let transaction = try!(conn.transaction());
if try!(M::find().one(&transaction)).is_some() {
tree.add_with_field::<F, _>("is already in use".into());
}
Ok(())
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment