diff --git a/ActiveRecord.php b/ActiveRecord.php
index 084587547..bbfb62bad 100644
--- a/ActiveRecord.php
+++ b/ActiveRecord.php
@@ -1,50 +1,60 @@
-get_model_directory();
- $root = realpath(isset($path) ? $path : '.');
-
- if (($namespaces = ActiveRecord\get_namespaces($class_name)))
- {
- $class_name = array_pop($namespaces);
- $directories = array();
-
- foreach ($namespaces as $directory)
- $directories[] = $directory;
-
- $root .= DIRECTORY_SEPARATOR . implode($directories, DIRECTORY_SEPARATOR);
- }
-
- $file = "$root/$class_name.php";
-
- if (file_exists($file))
- require_once $file;
-}
\ No newline at end of file
+get_model_directories();
+ $namespace_directory = '';
+ if (($namespaces = ActiveRecord\get_namespaces($class_name)))
+ {
+ $class_name = array_pop($namespaces);
+ $directories = array();
+
+ foreach ($namespaces as $directory)
+ $directories[] = $directory;
+
+ $namespace_directory = DIRECTORY_SEPARATOR . implode($directories, DIRECTORY_SEPARATOR);
+ }
+ $paths = count($paths) ? $paths : array('.');
+
+ foreach($paths as $path)
+ {
+ $root = realpath($path);
+ $file = "{$root}{$namespace_directory}/{$class_name}.php";
+
+ if (file_exists($file))
+ {
+ require $file;
+ return;
+ }
+ }
+}
diff --git a/README.md b/README.md
index 9f7fe5a4a..04389392a 100755
--- a/README.md
+++ b/README.md
@@ -63,14 +63,17 @@ Example:
```php
ActiveRecord\Config::initialize(function($cfg)
{
- $cfg->set_model_directory('/path/to/your/model_directory');
- $cfg->set_connections(
- array(
- 'development' => 'mysql://username:password@localhost/development_database_name',
- 'test' => 'mysql://username:password@localhost/test_database_name',
- 'production' => 'mysql://username:password@localhost/production_database_name'
- )
- );
+ $cfg->set_model_directories(array(
+ '/path/to/your/model_directory',
+ '/some/other/path/to/your/model_directory'
+ ));
+ $cfg->set_connections(
+ array(
+ 'development' => 'mysql://username:password@localhost/development_database_name',
+ 'test' => 'mysql://username:password@localhost/test_database_name',
+ 'production' => 'mysql://username:password@localhost/production_database_name'
+ )
+ );
});
```
diff --git a/examples/orders/models/Order.php b/examples/orders/models/Order.php
index 642943c4b..8c0464c28 100644
--- a/examples/orders/models/Order.php
+++ b/examples/orders/models/Order.php
@@ -34,4 +34,3 @@ public function apply_tax()
$this->tax = $this->price * $tax;
}
}
-?>
diff --git a/examples/orders/models/Payment.php b/examples/orders/models/Payment.php
index faef5ef91..777f2b6a0 100644
--- a/examples/orders/models/Payment.php
+++ b/examples/orders/models/Payment.php
@@ -6,4 +6,3 @@ class Payment extends ActiveRecord\Model
array('person'),
array('order'));
}
-?>
diff --git a/examples/orders/models/Person.php b/examples/orders/models/Person.php
index b2e586064..da8a6e550 100644
--- a/examples/orders/models/Person.php
+++ b/examples/orders/models/Person.php
@@ -10,4 +10,3 @@ class Person extends ActiveRecord\Model
static $validates_presence_of = array(
array('name'), array('state'));
}
-?>
diff --git a/examples/orders/orders.php b/examples/orders/orders.php
index 8222ad448..44815b820 100644
--- a/examples/orders/orders.php
+++ b/examples/orders/orders.php
@@ -76,4 +76,4 @@
foreach ($order->people as $person)
echo " payment of $$person->amount by " . $person->name . "\n";
-?>
+
diff --git a/examples/simple/simple.php b/examples/simple/simple.php
index 0acff65d2..537919d4d 100644
--- a/examples/simple/simple.php
+++ b/examples/simple/simple.php
@@ -14,4 +14,3 @@ class Book extends ActiveRecord\Model { }
});
print_r(Book::first()->attributes());
-?>
diff --git a/examples/simple/simple_with_options.php b/examples/simple/simple_with_options.php
index 1056435f7..6e42fca92 100644
--- a/examples/simple/simple_with_options.php
+++ b/examples/simple/simple_with_options.php
@@ -29,4 +29,3 @@ class Book extends ActiveRecord\Model
});
print_r(Book::first()->attributes());
-?>
diff --git a/lib/Column.php b/lib/Column.php
index db6c0a3a6..3e583ef31 100644
--- a/lib/Column.php
+++ b/lib/Column.php
@@ -1,201 +1,205 @@
- self::DATETIME,
- 'timestamp' => self::DATETIME,
- 'date' => self::DATE,
- 'time' => self::TIME,
-
- 'tinyint' => self::INTEGER,
- 'smallint' => self::INTEGER,
- 'mediumint' => self::INTEGER,
- 'int' => self::INTEGER,
- 'bigint' => self::INTEGER,
-
- 'float' => self::DECIMAL,
- 'double' => self::DECIMAL,
- 'numeric' => self::DECIMAL,
- 'decimal' => self::DECIMAL,
- 'dec' => self::DECIMAL);
-
- /**
- * The true name of this column.
- * @var string
- */
- public $name;
-
- /**
- * The inflected name of this columns .. hyphens/spaces will be => _.
- * @var string
- */
- public $inflected_name;
-
- /**
- * The type of this column: STRING, INTEGER, ...
- * @var integer
- */
- public $type;
-
- /**
- * The raw database specific type.
- * @var string
- */
- public $raw_type;
-
- /**
- * The maximum length of this column.
- * @var int
- */
- public $length;
-
- /**
- * True if this column allows null.
- * @var boolean
- */
- public $nullable;
-
- /**
- * True if this column is a primary key.
- * @var boolean
- */
- public $pk;
-
- /**
- * The default value of the column.
- * @var mixed
- */
- public $default;
-
- /**
- * True if this column is set to auto_increment.
- * @var boolean
- */
- public $auto_increment;
-
- /**
- * Name of the sequence to use for this column if any.
- * @var boolean
- */
- public $sequence;
-
- /**
- * Cast a value to an integer type safely
- *
- * This will attempt to cast a value to an integer,
- * unless its detected that the casting will cause
- * the number to overflow or lose precision, in which
- * case the number will be returned as a string, so
- * that large integers (BIGINTS, unsigned INTS, etc)
- * can still be stored without error
- *
- * This would ideally be done with bcmath or gmp, but
- * requiring a new PHP extension for a bug-fix is a
- * little ridiculous
- *
- * @param mixed $value The value to cast
- * @return int|string type-casted value
- */
- public static function castIntegerSafely($value)
- {
- if (is_int($value))
- return $value;
-
- // Its just a decimal number
- elseif (is_numeric($value) && floor($value) != $value)
- return (int) $value;
-
- // If adding 0 to a string causes a float conversion,
- // we have a number over PHP_INT_MAX
- elseif (is_string($value) && is_float($value + 0))
- return (string) $value;
-
- // If a float was passed and its greater than PHP_INT_MAX
- // (which could be wrong due to floating point precision)
- // We'll also check for equal to (>=) in case the precision
- // loss creates an overflow on casting
- elseif (is_float($value) && $value >= PHP_INT_MAX)
- return number_format($value, 0, '', '');
-
- return (int) $value;
- }
-
- /**
- * Casts a value to the column's type.
- *
- * @param mixed $value The value to cast
- * @param Connection $connection The Connection this column belongs to
- * @return mixed type-casted value
- */
- public function cast($value, $connection)
- {
- if ($value === null)
- return null;
-
- switch ($this->type)
- {
- case self::STRING: return (string)$value;
- case self::INTEGER: return static::castIntegerSafely($value);
- case self::DECIMAL: return (double)$value;
- case self::DATETIME:
- case self::DATE:
- if (!$value)
- return null;
-
- $date_class = Config::instance()->get_date_class();
-
- if ($value instanceof $date_class)
- return $value;
-
- if ($value instanceof \DateTime)
- return $date_class::createFromFormat(
- Connection::DATETIME_TRANSLATE_FORMAT,
- $value->format(Connection::DATETIME_TRANSLATE_FORMAT),
- $value->getTimezone()
- );
-
- return $connection->string_to_datetime($value);
- }
- return $value;
- }
-
- /**
- * Sets the $type member variable.
- * @return mixed
- */
- public function map_raw_type()
- {
- if ($this->raw_type == 'integer')
- $this->raw_type = 'int';
-
- if (array_key_exists($this->raw_type,self::$TYPE_MAPPING))
- $this->type = self::$TYPE_MAPPING[$this->raw_type];
- else
- $this->type = self::STRING;
-
- return $this->type;
- }
-}
+ self::DATETIME,
+ 'timestamp' => self::DATETIME,
+ 'date' => self::DATE,
+ 'time' => self::TIME,
+
+ 'boolean' => self::BOOLEAN,
+
+ 'tinyint' => self::INTEGER,
+ 'smallint' => self::INTEGER,
+ 'mediumint' => self::INTEGER,
+ 'int' => self::INTEGER,
+ 'bigint' => self::INTEGER,
+
+ 'float' => self::DECIMAL,
+ 'double' => self::DECIMAL,
+ 'numeric' => self::DECIMAL,
+ 'decimal' => self::DECIMAL,
+ 'dec' => self::DECIMAL);
+
+ /**
+ * The true name of this column.
+ * @var string
+ */
+ public $name;
+
+ /**
+ * The inflected name of this columns .. hyphens/spaces will be => _.
+ * @var string
+ */
+ public $inflected_name;
+
+ /**
+ * The type of this column: STRING, INTEGER, ...
+ * @var integer
+ */
+ public $type;
+
+ /**
+ * The raw database specific type.
+ * @var string
+ */
+ public $raw_type;
+
+ /**
+ * The maximum length of this column.
+ * @var int
+ */
+ public $length;
+
+ /**
+ * True if this column allows null.
+ * @var boolean
+ */
+ public $nullable;
+
+ /**
+ * True if this column is a primary key.
+ * @var boolean
+ */
+ public $pk;
+
+ /**
+ * The default value of the column.
+ * @var mixed
+ */
+ public $default;
+
+ /**
+ * True if this column is set to auto_increment.
+ * @var boolean
+ */
+ public $auto_increment;
+
+ /**
+ * Name of the sequence to use for this column if any.
+ * @var boolean
+ */
+ public $sequence;
+
+ /**
+ * Cast a value to an integer type safely
+ *
+ * This will attempt to cast a value to an integer,
+ * unless its detected that the casting will cause
+ * the number to overflow or lose precision, in which
+ * case the number will be returned as a string, so
+ * that large integers (BIGINTS, unsigned INTS, etc)
+ * can still be stored without error
+ *
+ * This would ideally be done with bcmath or gmp, but
+ * requiring a new PHP extension for a bug-fix is a
+ * little ridiculous
+ *
+ * @param mixed $value The value to cast
+ * @return int|string type-casted value
+ */
+ public static function castIntegerSafely($value)
+ {
+ if (is_int($value))
+ return $value;
+
+ // Its just a decimal number
+ elseif (is_numeric($value) && floor($value) != $value)
+ return (int) $value;
+
+ // If adding 0 to a string causes a float conversion,
+ // we have a number over PHP_INT_MAX
+ elseif (is_string($value) && is_float($value + 0))
+ return (string) $value;
+
+ // If a float was passed and its greater than PHP_INT_MAX
+ // (which could be wrong due to floating point precision)
+ // We'll also check for equal to (>=) in case the precision
+ // loss creates an overflow on casting
+ elseif (is_float($value) && $value >= PHP_INT_MAX)
+ return number_format($value, 0, '', '');
+
+ return (int) $value;
+ }
+
+ /**
+ * Casts a value to the column's type.
+ *
+ * @param mixed $value The value to cast
+ * @param Connection $connection The Connection this column belongs to
+ * @return mixed type-casted value
+ */
+ public function cast($value, $connection)
+ {
+ if ($value === null)
+ return null;
+
+ switch ($this->type)
+ {
+ case self::STRING: return (string)$value;
+ case self::INTEGER: return static::castIntegerSafely($value);
+ case self::DECIMAL: return (double)$value;
+ case self::BOOLEAN: return $connection->boolean_to_string($value);
+ case self::DATETIME:
+ case self::DATE:
+ if (!$value)
+ return null;
+
+ $date_class = Config::instance()->get_date_class();
+
+ if ($value instanceof $date_class)
+ return $value;
+
+ if ($value instanceof \DateTime)
+ return $date_class::createFromFormat(
+ Connection::DATETIME_TRANSLATE_FORMAT,
+ $value->format(Connection::DATETIME_TRANSLATE_FORMAT),
+ $value->getTimezone()
+ );
+
+ return $connection->string_to_datetime($value);
+ }
+ return $value;
+ }
+
+ /**
+ * Sets the $type member variable.
+ * @return mixed
+ */
+ public function map_raw_type()
+ {
+ if ($this->raw_type == 'integer')
+ $this->raw_type = 'int';
+
+ if (array_key_exists($this->raw_type,self::$TYPE_MAPPING))
+ $this->type = self::$TYPE_MAPPING[$this->raw_type];
+ else
+ $this->type = self::STRING;
+
+ return $this->type;
+ }
+}
diff --git a/lib/Config.php b/lib/Config.php
index 1be1eedee..376d2a27d 100644
--- a/lib/Config.php
+++ b/lib/Config.php
@@ -51,12 +51,12 @@ class Config extends Singleton
private $connections = array();
/**
- * Directory for the auto_loading of model classes.
+ * Array of directories for the auto_loading of model classes.
*
* @see activerecord_autoload
- * @var string
+ * @var array
*/
- private $model_directory;
+ private $model_directories = array();
/**
* Switch for logging.
@@ -87,6 +87,13 @@ class Config extends Singleton
*/
private $date_format = \DateTime::ISO8601;
+ /**
+ * Switch for use of StrongParameters
+ *
+ * @var bool
+ */
+ private $require_strong_parameters = false;
+
/**
* Allows config initialization using a closure.
*
@@ -118,13 +125,25 @@ public static function initialize(Closure $initializer)
}
/**
- * Sets the list of database connection strings.
+ * Sets the list of database connections. Can be an array of connection strings or an array of arrays.
*
*
* $config->set_connections(array(
* 'development' => 'mysql://username:password@127.0.0.1/database_name'));
*
*
+ *
+ * $config->set_connections(array(
+ * 'development' => array(
+ * 'adapter' => 'mysql',
+ * 'host' => '127.0.0.1',
+ * 'database' => 'database_name',
+ * 'username' => 'username',
+ * 'password' => 'password'
+ * )
+ * ));
+ *
+ *
* @param array $connections Array of connections
* @param string $default_connection Optionally specify the default_connection
* @return void
@@ -138,7 +157,16 @@ public function set_connections($connections, $default_connection=null)
if ($default_connection)
$this->set_default_connection($default_connection);
- $this->connections = $connections;
+ $this->connections = array_map(function($connection){
+ if(is_string($connection))
+ {
+ return ConnectionInfo::from_connection_url($connection);
+ }
+ else
+ {
+ return new ConnectionInfo($connection);
+ }
+ }, $connections);
}
/**
@@ -157,7 +185,7 @@ public function get_connections()
* @param string $name Name of connection to retrieve
* @return string connection info for specified connection name
*/
- public function get_connection($name)
+ public function get_connection_info($name)
{
if (array_key_exists($name, $this->connections))
return $this->connections[$name];
@@ -170,7 +198,7 @@ public function get_connection($name)
*
* @return string
*/
- public function get_default_connection_string()
+ public function get_default_connection_info()
{
return array_key_exists($this->default_connection,$this->connections) ?
$this->connections[$this->default_connection] : null;
@@ -200,26 +228,53 @@ public function set_default_connection($name)
/**
* Sets the directory where models are located.
*
- * @param string $dir Directory path containing your models
+ * @param string $directory Directory path containing your models
* @return void
*/
- public function set_model_directory($dir)
+ public function set_model_directory($directory)
{
- $this->model_directory = $dir;
+ $this->set_model_directories(array($directory));
}
-
+
/**
- * Returns the model directory.
+ * Returns the first model directory.
*
* @return string
- * @throws ConfigException if specified directory was not found
*/
public function get_model_directory()
{
- if ($this->model_directory && !file_exists($this->model_directory))
- throw new ConfigException('Invalid or non-existent directory: '.$this->model_directory);
+ $model_directories = $this->get_model_directories();
+ return array_shift($model_directories);
+ }
+
+ /**
+ * Sets the directories where models are located.
+ *
+ * @param array $directories Array with directory paths containing your models
+ * @return void
+ * @throws ConfigException if one of the model directories was not found
+ */
+ public function set_model_directories($directories)
+ {
+ if (!is_array($directories))
+ throw new ConfigException("Directories must be an array");
+
+ foreach($directories as $directory)
+ {
+ if(!file_exists($directory) || !is_dir($directory))
+ throw new ConfigException('Invalid or non-existent directory: '. $directory);
+ }
+ $this->model_directories = $directories;
+ }
- return $this->model_directory;
+ /**
+ * Returns the array of model directories.
+ *
+ * @return array
+ */
+ public function get_model_directories()
+ {
+ return $this->model_directories;
}
/**
@@ -330,4 +385,26 @@ public function set_cache($url, $options=array())
{
Cache::initialize($url,$options);
}
-}
\ No newline at end of file
+
+ /**
+ * Enable or disable use of StrongParameters
+ *
+ * @param bool $flag
+ * @return void
+ */
+ public function set_require_strong_parameters($flag)
+ {
+ $this->require_strong_parameters = $flag;
+ }
+
+ /**
+ * Returns whether or not to require StrongParameters
+ *
+ * @return bool
+ */
+ public function get_require_strong_parameters()
+ {
+ return $this->require_strong_parameters;
+ }
+
+}
diff --git a/lib/Connection.php b/lib/Connection.php
index 69e8a1b20..fc71cc4f8 100644
--- a/lib/Connection.php
+++ b/lib/Connection.php
@@ -54,10 +54,10 @@ abstract class Connection
*/
private $logger;
/**
- * The name of the protocol that is used.
+ * The name of the adapter that is used.
* @var string
*/
- public $protocol;
+ public $adapter;
/**
* Database's date format
* @var string
@@ -91,41 +91,49 @@ abstract class Connection
/**
* Retrieve a database connection.
*
- * @param string $connection_string_or_connection_name A database connection string (ex. mysql://user:pass@host[:port]/dbname)
- * Everything after the protocol:// part is specific to the connection adapter.
+ * @param string $connection_info_or_name A database connection string (ex. mysql://user:pass@host[:port]/dbname)
+ * Everything after the adapter:// part is specific to the connection adapter.
* OR
* A connection name that is set in ActiveRecord\Config
* If null it will use the default connection specified by ActiveRecord\Config->set_default_connection
* @return Connection
- * @see parse_connection_url
+ * @see ConnectionInfo::from_connection_url
*/
- public static function instance($connection_string_or_connection_name=null)
+ public static function instance($connection_info=null)
{
$config = Config::instance();
- if (strpos($connection_string_or_connection_name, '://') === false)
+ if (!($connection_info instanceof ConnectionInfo))
{
- $connection_string = $connection_string_or_connection_name ?
- $config->get_connection($connection_string_or_connection_name) :
- $config->get_default_connection_string();
+ // Connection instantiation using a connection array
+ if (is_array($connection_info))
+ {
+ $connection_info = new ConnectionInfo($connection_info);
+ }
+ // Connection instantiation using a connection url
+ else if (is_string($connection_info) && strpos($connection_info, '://') !== false)
+ {
+ $connection_info = ConnectionInfo::from_connection_url($connection_info);
+ }
+ // Connection instantiation using a connection name
+ else
+ {
+ $connection_info = $connection_info ?
+ $config->get_connection_info($connection_info) :
+ $config->get_default_connection_info();
+ }
}
- else
- $connection_string = $connection_string_or_connection_name;
- if (!$connection_string)
- throw new DatabaseException("Empty connection string");
-
- $info = static::parse_connection_url($connection_string);
- $fqclass = static::load_adapter_class($info->protocol);
+ $fqclass = static::load_adapter_class($connection_info->adapter);
try {
- $connection = new $fqclass($info);
- $connection->protocol = $info->protocol;
+ $connection = new $fqclass($connection_info);
+ $connection->adapter = $connection_info->adapter;
$connection->logging = $config->get_logging();
$connection->logger = $connection->logging ? $config->get_logger() : null;
- if (isset($info->charset))
- $connection->set_encoding($info->charset);
+ if (isset($connection_info->charset))
+ $connection->set_encoding($connection_info->charset);
} catch (PDOException $e) {
throw new DatabaseException($e);
}
@@ -151,92 +159,6 @@ private static function load_adapter_class($adapter)
return $fqclass;
}
- /**
- * Use this for any adapters that can take connection info in the form below
- * to set the adapters connection info.
- *
- *
- * protocol://username:password@host[:port]/dbname
- * protocol://urlencoded%20username:urlencoded%20password@host[:port]/dbname?decode=true
- * protocol://username:password@unix(/some/file/path)/dbname
- *
- *
- * Sqlite has a special syntax, as it does not need a database name or user authentication:
- *
- *
- * sqlite://file.db
- * sqlite://../relative/path/to/file.db
- * sqlite://unix(/absolute/path/to/file.db)
- * sqlite://windows(c%2A/absolute/path/to/file.db)
- *
- *
- * @param string $connection_url A connection URL
- * @return object the parsed URL as an object.
- */
- public static function parse_connection_url($connection_url)
- {
- $url = @parse_url($connection_url);
-
- if (!isset($url['host']))
- throw new DatabaseException('Database host must be specified in the connection string. If you want to specify an absolute filename, use e.g. sqlite://unix(/path/to/file)');
-
- $info = new \stdClass();
- $info->protocol = $url['scheme'];
- $info->host = $url['host'];
- $info->db = isset($url['path']) ? substr($url['path'], 1) : null;
- $info->user = isset($url['user']) ? $url['user'] : null;
- $info->pass = isset($url['pass']) ? $url['pass'] : null;
-
- $allow_blank_db = ($info->protocol == 'sqlite');
-
- if ($info->host == 'unix(')
- {
- $socket_database = $info->host . '/' . $info->db;
-
- if ($allow_blank_db)
- $unix_regex = '/^unix\((.+)\)\/?().*$/';
- else
- $unix_regex = '/^unix\((.+)\)\/(.+)$/';
-
- if (preg_match_all($unix_regex, $socket_database, $matches) > 0)
- {
- $info->host = $matches[1][0];
- $info->db = $matches[2][0];
- }
- } elseif (substr($info->host, 0, 8) == 'windows(')
- {
- $info->host = urldecode(substr($info->host, 8) . '/' . substr($info->db, 0, -1));
- $info->db = null;
- }
-
- if ($allow_blank_db && $info->db)
- $info->host .= '/' . $info->db;
-
- if (isset($url['port']))
- $info->port = $url['port'];
-
- if (strpos($connection_url, 'decode=true') !== false)
- {
- if ($info->user)
- $info->user = urldecode($info->user);
-
- if ($info->pass)
- $info->pass = urldecode($info->pass);
- }
-
- if (isset($url['query']))
- {
- foreach (explode('/&/', $url['query']) as $pair) {
- list($name, $value) = explode('=', $pair);
-
- if ($name == 'charset')
- $info->charset = $value;
- }
- }
-
- return $info;
- }
-
/**
* Class Connection is a singleton. Access it via instance().
*
@@ -257,7 +179,7 @@ protected function __construct($info)
else
$host = "unix_socket=$info->host";
- $this->connection = new PDO("$info->protocol:$host;dbname=$info->db", $info->user, $info->pass, static::$PDO_OPTIONS);
+ $this->connection = new PDO("$info->adapter:$host;dbname=$info->database", $info->username, $info->password, static::$PDO_OPTIONS);
} catch (PDOException $e) {
throw new DatabaseException($e);
}
@@ -499,6 +421,22 @@ public function string_to_datetime($string)
);
}
+ /**
+ * Converts a boolean value to a string representation.
+ *
+ * The converted string representation should be in a format acceptable by the
+ * underlying database connection.
+ *
+ * @param mixed $value
+ * @access public
+ * @return string
+ */
+ public function boolean_to_string($value)
+ {
+ $boolean = (boolean)$value;
+ return (string)$boolean;
+ }
+
/**
* Adds a limit clause to the SQL query.
*
diff --git a/lib/ConnectionInfo.php b/lib/ConnectionInfo.php
new file mode 100644
index 000000000..74081b2ea
--- /dev/null
+++ b/lib/ConnectionInfo.php
@@ -0,0 +1,152 @@
+ $value)
+ {
+ if(property_exists($this, $prop))
+ {
+ $this->{$prop} = $value;
+ }
+ }
+ }
+
+ /**
+ * Parses a connection url and return a ConnectionInfo object
+ *
+ * Use this for any adapters that can take connection info in the form below
+ * to set the adapters connection info.
+ *
+ *
+ * adapter://username:password@host[:port]/dbname
+ * adapter://urlencoded%20username:urlencoded%20password@host[:port]/dbname?decode=true
+ * adapter://username:password@unix(/some/file/path)/dbname
+ *
+ *
+ * Sqlite has a special syntax, as it does not need a database name or user authentication:
+ *
+ *
+ * sqlite://file.db
+ * sqlite://../relative/path/to/file.db
+ * sqlite://unix(/absolute/path/to/file.db)
+ * sqlite://windows(c%2A/absolute/path/to/file.db)
+ *
+ *
+ * @param string $connection_url A connection URL
+ * @return ConnectionInfo the parsed URL as an object.
+ */
+ public static function from_connection_url($connection_url)
+ {
+ $url = @parse_url($connection_url);
+
+ if (!isset($url['host']))
+ throw new DatabaseException('Database host must be specified in the connection string. If you want to specify an absolute filename, use e.g. sqlite://unix(/path/to/file)');
+
+ $info = new self();
+ $info->adapter = $url['scheme'];
+ $info->host = $url['host'];
+
+ $decode = false;
+
+ if (isset($url['query']))
+ {
+ parse_str($url['query'], $params);
+
+ if(isset($params['charset']))
+ $info->charset = $params['charset'];
+
+ if(isset($params['decode']))
+ $decode = ($params['decode'] == 'true');
+ }
+
+ if(isset($url['path']))
+ $info->database = substr($url['path'], 1);
+
+ $allow_blank_db = ($info->adapter == 'sqlite');
+
+ if ($info->host == 'unix(')
+ {
+ $socket_database = $info->host . '/' . $info->database;
+
+ sscanf($socket_database, 'unix(%[^)])/%s', $host, $database);
+
+ $info->host = $host;
+ $info->database = $database;
+ }
+ elseif (substr($info->host, 0, 8) == 'windows(')
+ {
+ $info->host = urldecode(substr($info->host, 8) . '/' . substr($info->database, 0, -1));
+ $info->database = null;
+ }
+ else
+ {
+ if ($allow_blank_db && $info->database)
+ $info->host .= '/' . $info->database;
+ }
+
+ if (isset($url['port']))
+ $info->port = $url['port'];
+
+ if (isset($url['user']))
+ $info->username = $decode ? urldecode($url['user']) : $url['user'];
+
+ if (isset($url['pass']))
+ $info->password = $decode ? urldecode($url['pass']) : $url['pass'];
+
+ return $info;
+ }
+
+}
diff --git a/lib/ConnectionManager.php b/lib/ConnectionManager.php
index 727c33dac..11ed5705a 100644
--- a/lib/ConnectionManager.php
+++ b/lib/ConnectionManager.php
@@ -30,7 +30,7 @@ public static function get_connection($name=null)
$name = $name ? $name : $config->get_default_connection();
if (!isset(self::$connections[$name]) || !self::$connections[$name]->connection)
- self::$connections[$name] = Connection::instance($config->get_connection($name));
+ self::$connections[$name] = Connection::instance($config->get_connection_info($name));
return self::$connections[$name];
}
diff --git a/lib/Exceptions.php b/lib/Exceptions.php
index d3de8ab22..d2ca7672b 100644
--- a/lib/Exceptions.php
+++ b/lib/Exceptions.php
@@ -141,3 +141,17 @@ class RelationshipException extends ActiveRecordException {}
* @package ActiveRecord
*/
class HasManyThroughAssociationException extends RelationshipException {}
+
+/**
+ * Thrown for unsafe in attributes in mass assignment
+ *
+ * @package ActiveRecord
+ */
+class UnsafeParametersException extends ActiveRecordException {}
+
+/**
+ * Thrown for non existing parameter from requireParam
+ *
+ * @package ActiveRecord
+ */
+class ParameterMissingException extends ActiveRecordException {}
diff --git a/lib/Expressions.php b/lib/Expressions.php
index 3fa2962fa..881fa4afd 100644
--- a/lib/Expressions.php
+++ b/lib/Expressions.php
@@ -1,192 +1,192 @@
-connection = $connection;
-
- if (is_array($expressions))
- {
- $glue = func_num_args() > 2 ? func_get_arg(2) : ' AND ';
- list($expressions,$values) = $this->build_sql_from_hash($expressions,$glue);
- }
-
- if ($expressions != '')
- {
- if (!$values)
- $values = array_slice(func_get_args(),2);
-
- $this->values = $values;
- $this->expressions = $expressions;
- }
- }
-
- /**
- * Bind a value to the specific one based index. There must be a bind marker
- * for each value bound or to_s() will throw an exception.
- */
- public function bind($parameter_number, $value)
- {
- if ($parameter_number <= 0)
- throw new ExpressionsException("Invalid parameter index: $parameter_number");
-
- $this->values[$parameter_number-1] = $value;
- }
-
- public function bind_values($values)
- {
- $this->values = $values;
- }
-
- /**
- * Returns all the values currently bound.
- */
- public function values()
- {
- return $this->values;
- }
-
- /**
- * Returns the connection object.
- */
- public function get_connection()
- {
- return $this->connection;
- }
-
- /**
- * Sets the connection object. It is highly recommended to set this so we can
- * use the adapter's native escaping mechanism.
- *
- * @param string $connection a Connection instance
- */
- public function set_connection($connection)
- {
- $this->connection = $connection;
- }
-
- public function to_s($substitute=false, &$options=null)
- {
- if (!$options) $options = array();
-
- $values = array_key_exists('values',$options) ? $options['values'] : $this->values;
-
- $ret = "";
- $replace = array();
- $num_values = count($values);
- $len = strlen($this->expressions);
- $quotes = 0;
-
- for ($i=0,$n=strlen($this->expressions),$j=0; $i<$n; ++$i)
- {
- $ch = $this->expressions[$i];
-
- if ($ch == self::ParameterMarker)
- {
- if ($quotes % 2 == 0)
- {
- if ($j > $num_values-1)
- throw new ExpressionsException("No bound parameter for index $j");
-
- $ch = $this->substitute($values,$substitute,$i,$j++);
- }
- }
- elseif ($ch == '\'' && $i > 0 && $this->expressions[$i-1] != '\\')
- ++$quotes;
-
- $ret .= $ch;
- }
- return $ret;
- }
-
- private function build_sql_from_hash(&$hash, $glue)
- {
- $sql = $g = "";
-
- foreach ($hash as $name => $value)
- {
- if ($this->connection)
- $name = $this->connection->quote_name($name);
-
- if (is_array($value))
- $sql .= "$g$name IN(?)";
- elseif (is_null($value))
- $sql .= "$g$name IS ?";
- else
- $sql .= "$g$name=?";
-
- $g = $glue;
- }
- return array($sql,array_values($hash));
- }
-
- private function substitute(&$values, $substitute, $pos, $parameter_index)
- {
- $value = $values[$parameter_index];
-
- if (is_array($value))
- {
- $value_count = count($value);
-
- if ($value_count === 0)
- if ($substitute)
- return 'NULL';
- else
- return self::ParameterMarker;
-
- if ($substitute)
- {
- $ret = '';
-
- for ($i=0, $n=$value_count; $i<$n; ++$i)
- $ret .= ($i > 0 ? ',' : '') . $this->stringify_value($value[$i]);
-
- return $ret;
- }
- return join(',',array_fill(0,$value_count,self::ParameterMarker));
- }
-
- if ($substitute)
- return $this->stringify_value($value);
-
- return $this->expressions[$pos];
- }
-
- private function stringify_value($value)
- {
- if (is_null($value))
- return "NULL";
-
- return is_string($value) ? $this->quote_string($value) : $value;
- }
-
- private function quote_string($value)
- {
- if ($this->connection)
- return $this->connection->escape($value);
-
- return "'" . str_replace("'","''",$value) . "'";
- }
+connection = $connection;
+
+ if (is_array($expressions))
+ {
+ $glue = func_num_args() > 2 ? func_get_arg(2) : ' AND ';
+ list($expressions,$values) = $this->build_sql_from_hash($expressions,$glue);
+ }
+
+ if ($expressions != '')
+ {
+ if (!$values)
+ $values = array_slice(func_get_args(),2);
+
+ $this->values = $values;
+ $this->expressions = $expressions;
+ }
+ }
+
+ /**
+ * Bind a value to the specific one based index. There must be a bind marker
+ * for each value bound or to_s() will throw an exception.
+ */
+ public function bind($parameter_number, $value)
+ {
+ if ($parameter_number <= 0)
+ throw new ExpressionsException("Invalid parameter index: $parameter_number");
+
+ $this->values[$parameter_number-1] = $value;
+ }
+
+ public function bind_values($values)
+ {
+ $this->values = $values;
+ }
+
+ /**
+ * Returns all the values currently bound.
+ */
+ public function values()
+ {
+ return $this->values;
+ }
+
+ /**
+ * Returns the connection object.
+ */
+ public function get_connection()
+ {
+ return $this->connection;
+ }
+
+ /**
+ * Sets the connection object. It is highly recommended to set this so we can
+ * use the adapter's native escaping mechanism.
+ *
+ * @param string $connection a Connection instance
+ */
+ public function set_connection($connection)
+ {
+ $this->connection = $connection;
+ }
+
+ public function to_s($substitute=false, &$options=null)
+ {
+ if (!$options) $options = array();
+
+ $values = array_key_exists('values',$options) ? $options['values'] : $this->values;
+
+ $ret = "";
+ $replace = array();
+ $num_values = count($values);
+ $len = strlen($this->expressions);
+ $quotes = 0;
+
+ for ($i=0,$n=strlen($this->expressions),$j=0; $i<$n; ++$i)
+ {
+ $ch = $this->expressions[$i];
+
+ if ($ch == self::ParameterMarker)
+ {
+ if ($quotes % 2 == 0)
+ {
+ if ($j > $num_values-1)
+ throw new ExpressionsException("No bound parameter for index $j");
+
+ $ch = $this->substitute($values,$substitute,$i,$j++);
+ }
+ }
+ elseif ($ch == '\'' && $i > 0 && $this->expressions[$i-1] != '\\')
+ ++$quotes;
+
+ $ret .= $ch;
+ }
+ return $ret;
+ }
+
+ private function build_sql_from_hash(&$hash, $glue)
+ {
+ $sql = $g = "";
+
+ foreach ($hash as $name => $value)
+ {
+ if ($this->connection)
+ $name = $this->connection->quote_name($name);
+
+ if (is_array($value))
+ $sql .= "$g$name IN(?)";
+ elseif (is_null($value))
+ $sql .= "$g$name IS ?";
+ else
+ $sql .= "$g$name=?";
+
+ $g = $glue;
+ }
+ return array($sql,array_values($hash));
+ }
+
+ private function substitute(&$values, $substitute, $pos, $parameter_index)
+ {
+ $value = $values[$parameter_index];
+
+ if (is_array($value))
+ {
+ $value_count = count($value);
+
+ if ($value_count === 0)
+ if ($substitute)
+ return 'NULL';
+ else
+ return self::ParameterMarker;
+
+ if ($substitute)
+ {
+ $ret = '';
+
+ for ($i=0, $n=$value_count; $i<$n; ++$i)
+ $ret .= ($i > 0 ? ',' : '') . $this->stringify_value($value[$i]);
+
+ return $ret;
+ }
+ return join(',',array_fill(0,$value_count,self::ParameterMarker));
+ }
+
+ if ($substitute)
+ return $this->stringify_value($value);
+
+ return $this->expressions[$pos];
+ }
+
+ private function stringify_value($value)
+ {
+ if (is_null($value))
+ return "NULL";
+
+ return is_string($value) ? $this->quote_string($value) : $value;
+ }
+
+ private function quote_string($value)
+ {
+ if ($this->connection)
+ return $this->connection->escape($value);
+
+ return "'" . str_replace("'","''",$value) . "'";
+ }
}
\ No newline at end of file
diff --git a/lib/Inflector.php b/lib/Inflector.php
index 732fb3422..cea9229c3 100644
--- a/lib/Inflector.php
+++ b/lib/Inflector.php
@@ -1,119 +1,119 @@
- 0)
- $camelized[0] = strtolower($camelized[0]);
-
- return $camelized;
- }
-
- /**
- * Determines if a string contains all uppercase characters.
- *
- * @param string $s string to check
- * @return bool
- */
- public static function is_upper($s)
- {
- return (strtoupper($s) === $s);
- }
-
- /**
- * Determines if a string contains all lowercase characters.
- *
- * @param string $s string to check
- * @return bool
- */
- public static function is_lower($s)
- {
- return (strtolower($s) === $s);
- }
-
- /**
- * Convert a camelized string to a lowercase, underscored string.
- *
- * @param string $s string to convert
- * @return string
- */
- public function uncamelize($s)
- {
- $normalized = '';
-
- for ($i=0,$n=strlen($s); $i<$n; ++$i)
- {
- if (ctype_alpha($s[$i]) && self::is_upper($s[$i]))
- $normalized .= '_' . strtolower($s[$i]);
- else
- $normalized .= $s[$i];
- }
- return trim($normalized,' _');
- }
-
- /**
- * Convert a string with space into a underscored equivalent.
- *
- * @param string $s string to convert
- * @return string
- */
- public function underscorify($s)
- {
- return preg_replace(array('/[_\- ]+/','/([a-z])([A-Z])/'),array('_','\\1_\\2'),trim($s));
- }
-
- public function keyify($class_name)
- {
- return strtolower($this->underscorify(denamespace($class_name))) . '_id';
- }
-
- abstract function variablize($s);
-}
-
-/**
- * @package ActiveRecord
- */
-class StandardInflector extends Inflector
-{
- public function tableize($s) { return Utils::pluralize(strtolower($this->underscorify($s))); }
- public function variablize($s) { return str_replace(array('-',' '),array('_','_'),strtolower(trim($s))); }
+ 0)
+ $camelized[0] = strtolower($camelized[0]);
+
+ return $camelized;
+ }
+
+ /**
+ * Determines if a string contains all uppercase characters.
+ *
+ * @param string $s string to check
+ * @return bool
+ */
+ public static function is_upper($s)
+ {
+ return (strtoupper($s) === $s);
+ }
+
+ /**
+ * Determines if a string contains all lowercase characters.
+ *
+ * @param string $s string to check
+ * @return bool
+ */
+ public static function is_lower($s)
+ {
+ return (strtolower($s) === $s);
+ }
+
+ /**
+ * Convert a camelized string to a lowercase, underscored string.
+ *
+ * @param string $s string to convert
+ * @return string
+ */
+ public function uncamelize($s)
+ {
+ $normalized = '';
+
+ for ($i=0,$n=strlen($s); $i<$n; ++$i)
+ {
+ if (ctype_alpha($s[$i]) && self::is_upper($s[$i]))
+ $normalized .= '_' . strtolower($s[$i]);
+ else
+ $normalized .= $s[$i];
+ }
+ return trim($normalized,' _');
+ }
+
+ /**
+ * Convert a string with space into a underscored equivalent.
+ *
+ * @param string $s string to convert
+ * @return string
+ */
+ public function underscorify($s)
+ {
+ return preg_replace(array('/[_\- ]+/','/([a-z])([A-Z])/'),array('_','\\1_\\2'),trim($s));
+ }
+
+ public function keyify($class_name)
+ {
+ return strtolower($this->underscorify(denamespace($class_name))) . '_id';
+ }
+
+ abstract function variablize($s);
+}
+
+/**
+ * @package ActiveRecord
+ */
+class StandardInflector extends Inflector
+{
+ public function tableize($s) { return Utils::pluralize(strtolower($this->underscorify($s))); }
+ public function variablize($s) { return str_replace(array('-',' '),array('_','_'),strtolower(trim($s))); }
}
\ No newline at end of file
diff --git a/lib/Model.php b/lib/Model.php
index 28bcd68bf..76a1e399a 100644
--- a/lib/Model.php
+++ b/lib/Model.php
@@ -87,6 +87,23 @@ class Model
*/
private $attributes = array();
+ /**
+ * Contains changes made to model attributes as
+ * $attribute_name => array($original_value, $current_value)
+ *
+ * @var array
+ * @access private
+ */
+ private $changed_attributes = array();
+
+ /**
+ * Contains changes made to model attributes before it was saved
+ *
+ * @var array
+ * @access private
+ */
+ private $previously_changed = array();
+
/**
* Flag whether or not this model's attributes have been modified since it will either be null or an array of column_names that have been modified
*
@@ -115,6 +132,13 @@ class Model
*/
private $__new_record = true;
+ /**
+ * Flag whether or not this model has been validated
+ *
+ * @var boolean
+ */
+ private $__validated = false;
+
/**
* Set to the name of the connection this {@link Model} should use.
*
@@ -255,13 +279,14 @@ class Model
* new Person(array('first_name' => 'Tito', 'last_name' => 'the Grief'));
*
*
- * @param array $attributes Hash containing names and values to mass assign to the model
+ * @param array $attributes Hash containing names and values to mass assign to the model or
+ * StrongParameters object
* @param boolean $guard_attributes Set to true to guard protected/non-accessible attributes
* @param boolean $instantiating_via_find Set to true if this model is being created from a find call
* @param boolean $new_record Set to true if this should be considered a new record
* @return Model
*/
- public function __construct(array $attributes=array(), $guard_attributes=true, $instantiating_via_find=false, $new_record=true)
+ public function __construct($attributes=null, $guard_attributes=true, $instantiating_via_find=false, $new_record=true)
{
$this->__new_record = $new_record;
@@ -413,7 +438,7 @@ public function __set($name, $value)
if (array_key_exists($name, static::$alias_attribute))
$name = static::$alias_attribute[$name];
- elseif (method_exists($this,"set_$name"))
+ if (method_exists($this,"set_$name"))
{
$name = "set_$name";
return $this->$name($value);
@@ -477,8 +502,17 @@ public function assign_attribute($name, $value)
// has the ability to flag this model as dirty if a field in the Date object changes.
$value->attribute_of($this,$name);
- $this->attributes[$name] = $value;
- $this->flag_dirty($name);
+ // only update the attribute if it isn't set or has changed
+ if (!isset($this->attributes[$name]) || ($this->attributes[$name] !== $value)) {
+ // track changes to the attribute
+ if (array_key_exists($name, $this->attributes) && !isset($this->changed_attributes[$name]))
+ $this->changed_attributes[$name] = $this->attributes[$name];
+
+ // set the attribute and flag as dirty
+ $this->attributes[$name] = $value;
+ $this->flag_dirty($name);
+ $this->reset_validated();
+ }
return $value;
}
@@ -498,8 +532,10 @@ public function &read_attribute($name)
$name = static::$alias_attribute[$name];
// check for attribute
- if (array_key_exists($name,$this->attributes))
- return $this->attributes[$name];
+ if (array_key_exists($name,$this->attributes)) {
+ $value = $this->attributes[$name];
+ return $value;
+ }
// check relationships if no attribute
if (array_key_exists($name,$this->__relationships))
@@ -589,6 +625,52 @@ public function attributes()
return $this->attributes;
}
+ /**
+ * Returns a copy of the model's changed attributes hash with the
+ * attribute name and the original value.
+ *
+ * @return array A copy of the model's changed attribute data
+ */
+ public function changed_attributes()
+ {
+ return $this->changed_attributes;
+ }
+
+ /**
+ * Returns a copy of the model's changed attributes as a hash
+ * in the form $attribute => array($original_value, $current_value)
+ *
+ * @return array A copy of the model's attribute changes
+ */
+ public function changes()
+ {
+ $changes = array();
+ $attributes = array_intersect_key($this->attributes, $this->changed_attributes);
+ foreach($attributes as $name => $value) {
+ $changes[$name] = array($this->changed_attributes[$name],$value);
+ }
+ return $changes;
+ }
+
+ /**
+ * Returns a copy of the model's changed attributes before it was saved
+ *
+ * @return array A copy of the model's changed attribute before a save
+ */
+ public function previous_changes()
+ {
+ return $this->previously_changed;
+ }
+
+ /**
+ * Returns the value of an attribute before it was changed
+ *
+ * @return string The original value of an attribute
+ */
+ public function attribute_was($name) {
+ return isset($this->changed_attributes[$name]) ? $this->changed_attributes[$name] : null;
+ }
+
/**
* Retrieve the primary key name.
*
@@ -810,7 +892,7 @@ private function insert($validate=true)
{
$this->verify_not_readonly('insert');
- if (($validate && !$this->_validate() || !$this->invoke_callback('before_create',false)))
+ if (($validate && !$this->_validate()) || !$this->invoke_callback('before_create',false))
return false;
$table = static::table();
@@ -1081,8 +1163,12 @@ private function _validate()
{
require_once 'Validations.php';
+ $this->flag_validated();
+
$validator = new Validations($this);
- $validation_on = 'validation_on_' . ($this->is_new_record() ? 'create' : 'update');
+ $validation_mode = $this->is_new_record() ? 'create' : 'update';
+ $validation_on = 'validation_on_' . $validation_mode;
+ $validator->set_validation_mode($validation_mode);
foreach (array('before_validation', "before_$validation_on") as $callback)
{
@@ -1103,6 +1189,36 @@ private function _validate()
return true;
}
+ /**
+ * Returns true if the model has been validated.
+ *
+ * @return boolean true if validated
+ */
+ private function is_validated()
+ {
+ return $this->__validated;
+ }
+
+ /**
+ * Flag model as validated
+ *
+ * @return void
+ */
+ private function flag_validated()
+ {
+ $this->__validated = true;
+ }
+
+ /**
+ * Resets the validated flag.
+ *
+ * @return void
+ */
+ private function reset_validated()
+ {
+ $this->__validated = false;
+ }
+
/**
* Returns true if the model has been modified.
*
@@ -1114,25 +1230,30 @@ public function is_dirty()
}
/**
- * Run validations on model and returns whether or not model passed validation.
+ * Returns true if the model is valid.
*
+ * @param boolean $force_validation If true, will always run validations.
* @see is_invalid
* @return boolean
*/
- public function is_valid()
+ public function is_valid($force_validation = false)
{
- return $this->_validate();
+ if( $force_validation || !$this->is_validated() )
+ return $this->_validate();
+
+ return $this->errors->is_empty();
}
/**
- * Runs validations and returns true if invalid.
+ * Returns true if the model is invalid.
*
+ * @param boolean $force_validation If true, will always run validations.
* @see is_valid
* @return boolean
*/
- public function is_invalid()
+ public function is_invalid($force_validation = false)
{
- return !$this->_validate();
+ return !$this->is_valid($force_validation);
}
/**
@@ -1150,9 +1271,10 @@ public function set_timestamps()
}
/**
- * Mass update the model with an array of attribute data and saves to the database.
+ * Mass update the model with attribute data and saves to the database.
*
- * @param array $attributes An attribute data array in the form array(name => value, ...)
+ * @param StrongParameters|array $attributes An StrongParameters object or array
+ * containing data to update in the form array(name => value, ...)
* @return boolean True if successfully updated and saved otherwise false
*/
public function update_attributes($attributes)
@@ -1175,15 +1297,16 @@ public function update_attribute($name, $value)
}
/**
- * Mass update the model with data from an attributes hash.
+ * Mass update the model with data from an attributes hash or object.
*
* Unlike update_attributes() this method only updates the model's data
* but DOES NOT save it to the database.
*
* @see update_attributes
- * @param array $attributes An array containing data to update in the form array(name => value, ...)
+ * @param StrongParameters|array $attributes An StrongParameters object or array
+ * containing data to update in the form array(name => value, ...)
*/
- public function set_attributes(array $attributes)
+ public function set_attributes($attributes)
{
$this->set_attributes_via_mass_assignment($attributes, true);
}
@@ -1192,35 +1315,48 @@ public function set_attributes(array $attributes)
* Passing $guard_attributes as true will throw an exception if an attribute does not exist.
*
* @throws \ActiveRecord\UndefinedPropertyException
- * @param array $attributes An array in the form array(name => value, ...)
+ * @param StrongParameters|array $attributes An StrongParameters object or array
+ * containing data to update in the form array(name => value, ...)
* @param boolean $guard_attributes Flag of whether or not protected/non-accessible attributes should be guarded
*/
- private function set_attributes_via_mass_assignment(array &$attributes, $guard_attributes)
+ private function set_attributes_via_mass_assignment(&$attributes, $guard_attributes)
{
- //access uninflected columns since that is what we would have in result set
+ // return fast when no attributes are given
+ if(empty($attributes)) return;
+
+ $require_strong_parameters = Config::instance()->get_require_strong_parameters();
+ if($guard_attributes && $require_strong_parameters && !($attributes instanceof StrongParameters))
+ {
+ throw new UnsafeParametersException();
+ }
+
+ // Legacy support for attr_accessible/attr_protected
+ // Recommended way of protecting attributes is by using StrongParameters
+ if($guard_attributes && !($attributes instanceof StrongParameters))
+ {
+ if (!empty(static::$attr_accessible))
+ $attributes = array_intersect_key($attributes, array_flip(static::$attr_accessible));
+
+ if (!empty(static::$attr_protected))
+ $attributes = array_diff_key($attributes, array_flip(static::$attr_protected));
+ }
+
$table = static::table();
$exceptions = array();
- $use_attr_accessible = !empty(static::$attr_accessible);
- $use_attr_protected = !empty(static::$attr_protected);
$connection = static::connection();
+ // access uninflected columns since that is what we would have in result set
foreach ($attributes as $name => $value)
{
// is a normal field on the table
- if (array_key_exists($name,$table->columns))
+ if (array_key_exists($name, $table->columns))
{
- $value = $table->columns[$name]->cast($value,$connection);
+ $value = $table->columns[$name]->cast($value, $connection);
$name = $table->columns[$name]->inflected_name;
}
if ($guard_attributes)
{
- if ($use_attr_accessible && !in_array($name,static::$attr_accessible))
- continue;
-
- if ($use_attr_protected && in_array($name,static::$attr_protected))
- continue;
-
// set valid table data
try {
$this->$name = $value;
@@ -1235,7 +1371,7 @@ private function set_attributes_via_mass_assignment(array &$attributes, $guard_a
continue;
// set arbitrary data
- $this->assign_attribute($name,$value);
+ $this->assign_attribute($name, $value);
}
}
@@ -1285,6 +1421,7 @@ public function reload()
$this->expire_cache();
$this->set_attributes_via_mass_assignment($this->find($pk)->attributes, false);
$this->reset_dirty();
+ $this->reset_validated();
return $this;
}
@@ -1293,6 +1430,7 @@ public function __clone()
{
$this->__relationships = array();
$this->reset_dirty();
+ $this->reset_validated();
return $this;
}
@@ -1301,9 +1439,11 @@ public function __clone()
*
* @see dirty_attributes
*/
- public function reset_dirty()
+ public function reset_dirty($model_was_saved=false)
{
$this->__dirty = null;
+ $this->previously_changed = $model_was_saved ? $this->changes() : array();
+ $this->changed_attributes = array();
}
/**
@@ -1463,7 +1603,7 @@ public static function count(/* ... */)
$table = static::table();
$sql = $table->options_to_sql($options);
$values = $sql->get_where_values();
- return static::connection()->query_and_fetch_one($sql->to_s(),$values);
+ return (int) static::connection()->query_and_fetch_one($sql->to_s(),$values);
}
/**
diff --git a/lib/Relationship.php b/lib/Relationship.php
index 9846b44a4..16423609e 100644
--- a/lib/Relationship.php
+++ b/lib/Relationship.php
@@ -496,7 +496,7 @@ public function load(Model $model)
if ($this->through)
{
// verify through is a belongs_to or has_many for access of keys
- if (!($through_relationship = $this->get_table()->get_relationship($this->through)))
+ if (!($through_relationship = $model->table()->get_relationship($this->through)))
throw new HasManyThroughAssociationException("Could not find the association $this->through in model " . get_class($model));
if (!($through_relationship instanceof HasMany) && !($through_relationship instanceof BelongsTo))
@@ -508,8 +508,7 @@ public function load(Model $model)
$this->set_keys($this->get_table()->class->getName(), true);
- $class = $this->class_name;
- $relation = $class::table()->get_relationship($this->through);
+ $relation = $model::table()->get_relationship($this->through);
$through_table = $relation->get_table();
$this->options['joins'] = $this->construct_inner_join_sql($through_table, true);
diff --git a/lib/SQLBuilder.php b/lib/SQLBuilder.php
index ef239ed87..39c4daafa 100644
--- a/lib/SQLBuilder.php
+++ b/lib/SQLBuilder.php
@@ -291,7 +291,6 @@ private function prepend_table_name_to_fields($hash=array())
private function apply_where_conditions($args)
{
- require_once 'Expressions.php';
$num_args = count($args);
if ($num_args == 1 && is_hash($args[0]))
@@ -345,7 +344,6 @@ private function build_delete()
private function build_insert()
{
- require_once 'Expressions.php';
$keys = join(',',$this->quoted_key_names());
if ($this->sequence)
diff --git a/lib/Serialization.php b/lib/Serialization.php
index 812ce5b55..b4b9b324e 100644
--- a/lib/Serialization.php
+++ b/lib/Serialization.php
@@ -136,10 +136,13 @@ private function check_methods()
{
$this->options_to_a('methods');
- foreach ($this->options['methods'] as $method)
+ foreach ($this->options['methods'] as $method => $name)
{
+ if (is_numeric($method))
+ $method = $name;
+
if (method_exists($this->model, $method))
- $this->attributes[$method] = $this->model->$method();
+ $this->attributes[$name] = $this->model->$method();
}
}
}
@@ -242,7 +245,7 @@ final public function __toString()
* @return string
*/
abstract public function to_s();
-};
+}
/**
* Array serializer.
@@ -345,32 +348,32 @@ private function write($data, $tag=null)
*/
class CsvSerializer extends Serialization
{
- public static $delimiter = ',';
- public static $enclosure = '"';
-
- public function to_s()
- {
- if (@$this->options['only_header'] == true) return $this->header();
- return $this->row();
- }
-
- private function header()
- {
- return $this->to_csv(array_keys($this->to_a()));
- }
-
- private function row()
- {
- return $this->to_csv($this->to_a());
- }
-
- private function to_csv($arr)
- {
- $outstream = fopen('php://temp', 'w');
- fputcsv($outstream, $arr, self::$delimiter, self::$enclosure);
- rewind($outstream);
- $buffer = trim(stream_get_contents($outstream));
- fclose($outstream);
- return $buffer;
- }
-}
\ No newline at end of file
+ public static $delimiter = ',';
+ public static $enclosure = '"';
+
+ public function to_s()
+ {
+ if (@$this->options['only_header'] == true) return $this->header();
+ return $this->row();
+ }
+
+ private function header()
+ {
+ return $this->to_csv(array_keys($this->to_a()));
+ }
+
+ private function row()
+ {
+ return $this->to_csv($this->to_a());
+ }
+
+ private function to_csv($arr)
+ {
+ $outstream = fopen('php://temp', 'w');
+ fputcsv($outstream, $arr, self::$delimiter, self::$enclosure);
+ rewind($outstream);
+ $buffer = trim(stream_get_contents($outstream));
+ fclose($outstream);
+ return $buffer;
+ }
+}
diff --git a/lib/StrongParameters.php b/lib/StrongParameters.php
new file mode 100644
index 000000000..45f42161b
--- /dev/null
+++ b/lib/StrongParameters.php
@@ -0,0 +1,214 @@
+params = new ActiveRecord\StrongParameters(array_merge($_GET, $_POST));
+ * }
+ *
+ * This example assumes that your request data is structured as follows;
+ *
+ * array(
+ * "id" => 1,
+ * "user" => array(
+ * "name" => "Foo Bar",
+ * "bio" => "I'm Foo Bar",
+ * "email" => "foo@bar.baz"
+ * )
+ * )
+ *
+ * And then to use the data in a controller:
+ *
+ * public function update_profile()
+ * {
+ * $user = User::find($this->params['id']);
+ * $user->update_attributes($this->user_params());
+ * $this->redirect('back');
+ * }
+ *
+ * protected function user_params()
+ * {
+ * return $this->params->require_param('user')->permit('name', 'bio', 'email');
+ * }
+ *
+ *
+ * @package ActiveRecord
+ */
+class StrongParameters implements IteratorAggregate, ArrayAccess
+{
+ /**
+ * Array containing data
+ *
+ * @var array
+ */
+ protected $data = array();
+
+ /**
+ * Array containing permitted attributes
+ *
+ * @var array
+ */
+ protected $permitted = array();
+
+ /**
+ * Construct StrongParameters object with data
+ *
+ * @param array $data
+ * @return void
+ */
+ public function __construct(array $data)
+ {
+ $this->data = $this->parse($data);
+ }
+
+ /**
+ * Recursively parse array into StrongParameter
+ * @param array $data
+ * @return array
+ */
+ protected function parse(array $data)
+ {
+ return array_map(function($value)
+ {
+ if (!is_hash($value))
+ {
+ return $value;
+ }
+ return new StrongParameters($value);
+ }, $data);
+ }
+
+ /**
+ * Permit the specified attributes to be returned for mass assignment.
+ *
+ * @param mixed $attrs,...
+ * @return this
+ */
+ public function permit($attrs = array()/* [, ...$attr] */)
+ {
+ if(func_num_args() > 1)
+ {
+ $attrs = func_get_args();
+ }
+ elseif(!is_array($attrs))
+ {
+ $attrs = array($attrs);
+ }
+ $this->permitted = $attrs;
+ return $this;
+ }
+
+ /**
+ * Fetch a value from the data
+ *
+ * @param string $key
+ * @return mixed
+ */
+ public function fetch($key)
+ {
+ if (isset($this->data[$key])) {
+ return $this->data[$key];
+ }
+ return null;
+ }
+
+ /**
+ * Fetch a required value from the data. If the param doesn't exist an error
+ * is thrown
+ *
+ * @param string $key
+ * @throws ActiveRecord\ParameterMissingException
+ * @return mixed
+ */
+ public function requireParam($key)
+ {
+ $param = $this->fetch($key);
+ if (empty($param)) {
+ throw new ParameterMissingException("Missing param '$key'");
+ }
+ return $param;
+ }
+
+ /**
+ * @see requireParam
+ */
+ public function require_param($key)
+ {
+ return $this->requireParam($key);
+ }
+
+ /**
+ * Required method for IteratorAggregate interface.
+ *
+ * @return ArrayIterator iterator for permitted data.
+ */
+ public function getIterator()
+ {
+ $permitted_data = array_intersect_key($this->data, array_flip($this->permitted));
+ return new ArrayIterator($permitted_data);
+ }
+
+ /**
+ * Required method for ArrayAccess interface.
+ *
+ * @param string $offset
+ * @return bool true if offset exists
+ */
+ public function offsetExists($offset)
+ {
+ return isset($this->data[$offset]);
+ }
+
+ /**
+ * Required method for ArrayAccess interface.
+ *
+ * @param string $offset
+ * @return mixed
+ * @see fetch
+ */
+ public function offsetGet($offset)
+ {
+ return $this->fetch($offset);
+ }
+
+ /**
+ * Required method for ArrayAccess interface.
+ *
+ * @param string $offset
+ * @param mixed $value
+ * @return void
+ * @throws InvalidArgumentException when offset is null
+ */
+ public function offsetSet($offset, $value)
+ {
+ if (is_null($offset)) {
+ throw new InvalidArgumentException('offset cannot be null');
+ }
+ $this->data[$offset] = $value;
+ }
+
+ /**
+ * Required method for ArrayAccess interface.
+ *
+ * @param string $offset
+ * @return void
+ */
+ public function offsetUnset($offset)
+ {
+ unset($this->data[$offset]);
+ }
+
+}
diff --git a/lib/Table.php b/lib/Table.php
index 7434ff11c..ec6303459 100644
--- a/lib/Table.php
+++ b/lib/Table.php
@@ -98,7 +98,7 @@ public function __construct($class_name)
$this->callback = new CallBack($class_name);
$this->callback->register('before_save', function(Model $model) { $model->set_timestamps(); }, array('prepend' => true));
- $this->callback->register('after_save', function(Model $model) { $model->reset_dirty(); }, array('prepend' => true));
+ $this->callback->register('after_save', function(Model $model) { $model->reset_dirty(true); }, array('prepend' => true));
}
public function reestablish_connection($close=true)
@@ -458,6 +458,8 @@ private function set_primary_key()
$this->pk[] = $c->inflected_name;
}
}
+ // make sure the primary key is stored in lowercase
+ $this->pk = array_map('strtolower', $this->pk);
}
private function set_table_name()
diff --git a/lib/Utils.php b/lib/Utils.php
index 105d28c3a..3251f776c 100644
--- a/lib/Utils.php
+++ b/lib/Utils.php
@@ -63,13 +63,15 @@ function array_flatten(array $array)
/**
* Somewhat naive way to determine if an array is a hash.
*/
-function is_hash(&$array)
+function is_hash($array)
{
- if (!is_array($array))
+ if (!is_array($array)) {
return false;
+ }
$keys = array_keys($array);
- return @is_string($keys[0]) ? true : false;
+
+ return isset($keys[0]) && is_string($keys[0]);
}
/**
@@ -173,7 +175,7 @@ public static function extract_options($options)
return is_array(end($options)) ? end($options) : array();
}
- public static function add_condition(&$conditions=array(), $condition, $conjuction='AND')
+ public static function add_condition(&$conditions=array(), $condition, $conjunction='AND')
{
if (is_array($condition))
{
@@ -181,12 +183,12 @@ public static function add_condition(&$conditions=array(), $condition, $conjucti
$conditions = array_flatten($condition);
else
{
- $conditions[0] .= " $conjuction " . array_shift($condition);
+ $conditions[0] = "({$conditions[0]}) $conjunction (" . array_shift($condition) . ")";
$conditions[] = array_flatten($condition);
}
}
elseif (is_string($condition))
- $conditions[0] .= " $conjuction $condition";
+ $conditions[0] = "({$conditions[0]}) {$conjunction} ($condition)";
return $conditions;
}
@@ -367,4 +369,4 @@ public static function add_irregular($singular, $plural)
{
self::$irregular[$singular] = $plural;
}
-}
\ No newline at end of file
+}
diff --git a/lib/Validations.php b/lib/Validations.php
index 64a58cfa5..c2cec72b3 100644
--- a/lib/Validations.php
+++ b/lib/Validations.php
@@ -45,6 +45,7 @@ class Validations
private $options = array();
private $validators = array();
private $record;
+ private $validation_mode = null;
private static $VALIDATION_FUNCTIONS = array(
'validates_presence_of',
@@ -179,7 +180,9 @@ public function validates_presence_of($attrs)
foreach ($attrs as $attr)
{
$options = array_merge($configuration, $attr);
- $this->record->add_on_blank($options[0], $options['message']);
+
+ if( $this->validation_mode_matches($options['on']) )
+ $this->record->add_on_blank($options[0], $options['message']);
}
}
@@ -249,11 +252,17 @@ public function validates_exclusion_of($attrs)
*/
public function validates_inclusion_or_exclusion_of($type, $attrs)
{
- $configuration = array_merge(self::$DEFAULT_VALIDATION_OPTIONS, array('message' => Errors::$DEFAULT_ERROR_MESSAGES[$type], 'on' => 'save'));
+ $configuration = array_merge(self::$DEFAULT_VALIDATION_OPTIONS, array(
+ 'message' => Errors::$DEFAULT_ERROR_MESSAGES[$type]
+ ));
foreach ($attrs as $attr)
{
$options = array_merge($configuration, $attr);
+
+ if (!$this->validation_mode_matches($options['on']))
+ continue;
+
$attribute = $options[0];
$var = $this->model->$attribute;
@@ -313,6 +322,10 @@ public function validates_numericality_of($attrs)
foreach ($attrs as $attr)
{
$options = array_merge($configuration, $attr);
+
+ if (!$this->validation_mode_matches($options['on']))
+ continue;
+
$attribute = $options[0];
$var = $this->model->$attribute;
@@ -414,11 +427,18 @@ public function validates_size_of($attrs)
*/
public function validates_format_of($attrs)
{
- $configuration = array_merge(self::$DEFAULT_VALIDATION_OPTIONS, array('message' => Errors::$DEFAULT_ERROR_MESSAGES['invalid'], 'on' => 'save', 'with' => null));
+ $configuration = array_merge(self::$DEFAULT_VALIDATION_OPTIONS, array(
+ 'message' => Errors::$DEFAULT_ERROR_MESSAGES['invalid'],
+ 'with' => null
+ ));
foreach ($attrs as $attr)
{
$options = array_merge($configuration, $attr);
+
+ if (!$this->validation_mode_matches($options['on']))
+ continue;
+
$attribute = $options[0];
$var = $this->model->$attribute;
@@ -470,6 +490,10 @@ public function validates_length_of($attrs)
foreach ($attrs as $attr)
{
$options = array_merge($configuration, $attr);
+
+ if (!$this->validation_mode_matches($options['on']))
+ continue;
+
$range_options = array_intersect(array_keys(self::$ALL_RANGE_OPTIONS), array_keys($attr));
sort($range_options);
@@ -571,6 +595,10 @@ public function validates_uniqueness_of($attrs)
foreach ($attrs as $attr)
{
$options = array_merge($configuration, $attr);
+
+ if (!$this->validation_mode_matches($options['on']))
+ continue;
+
$pk = $this->model->get_primary_key();
$pk_value = $this->model->{$pk[0]};
@@ -610,6 +638,49 @@ public function validates_uniqueness_of($attrs)
$this->record->add($add_record, $options['message']);
}
}
+
+ /**
+ * Set the validation mode.
+ *
+ * The validation mode is used to check if a validation rule should be executed.
+ * This is decided based on the "on" option for validation rules.
+ *
+ * The "on" option can have the following values:
+ *
+ *
+ * - save: Invoked before saving changes to either a new or an existing model.
+ *
- create: Invoked only before inserting a new model.
+ *
- update: Invoked only before updating an existing model.
+ *
- delete: Invoked before deletion of a model (unimplemented).
+ *
+ *
+ * @param string $mode The mode to set, one of "create", "update" or "delete".
+ * @todo implement "delete" value for option "on"
+ */
+ public function set_validation_mode($mode)
+ {
+ $this->validation_mode = $mode;
+ }
+
+ /**
+ * Check if the current validation mode matches the specified mode.
+ *
+ * See {@link set_validation_mode} for more information on validation modes.
+ *
+ * @param string $mode The mode to be matched, one of "create", "update" or "delete".
+ * @return boolean
+ */
+ private function validation_mode_matches($mode)
+ {
+ // The validation mode is not always set, run the validation anyway in that case
+ if( $this->validation_mode == null )
+ return true;
+
+ if( $mode == 'save' )
+ return in_array($this->validation_mode, array('create', 'update'));
+
+ return $mode == $this->validation_mode;
+ }
private function is_null_with_option($var, &$options)
{
diff --git a/lib/adapters/MysqlAdapter.php b/lib/adapters/MysqlAdapter.php
index 12a50db63..ddd545059 100644
--- a/lib/adapters/MysqlAdapter.php
+++ b/lib/adapters/MysqlAdapter.php
@@ -96,4 +96,3 @@ public function native_database_types()
}
}
-?>
diff --git a/lib/adapters/OciAdapter.php b/lib/adapters/OciAdapter.php
index 353fffefa..5e97343e2 100644
--- a/lib/adapters/OciAdapter.php
+++ b/lib/adapters/OciAdapter.php
@@ -22,7 +22,7 @@ protected function __construct($info)
{
try {
$this->dsn_params = isset($info->charset) ? ";charset=$info->charset" : "";
- $this->connection = new PDO("oci:dbname=//$info->host/$info->db$this->dsn_params",$info->user,$info->pass,static::$PDO_OPTIONS);
+ $this->connection = new PDO("oci:dbname=//$info->host/$info->database$this->dsn_params",$info->username,$info->password,static::$PDO_OPTIONS);
} catch (PDOException $e) {
throw new DatabaseException($e);
}
@@ -143,4 +143,3 @@ public function native_database_types()
);
}
}
-?>
diff --git a/lib/adapters/PgsqlAdapter.php b/lib/adapters/PgsqlAdapter.php
index 72da44182..17e7eb113 100644
--- a/lib/adapters/PgsqlAdapter.php
+++ b/lib/adapters/PgsqlAdapter.php
@@ -101,7 +101,7 @@ public function create_column(&$column)
$c->map_raw_type();
- if ($column['default'])
+ if (!is_null($column['default']))
{
preg_match("/^nextval\('(.*)'\)$/",$column['default'],$matches);
@@ -131,9 +131,21 @@ public function native_database_types()
'time' => array('name' => 'time'),
'date' => array('name' => 'date'),
'binary' => array('name' => 'binary'),
- 'boolean' => array('name' => 'boolean')
+ 'boolean' => array('name' => 'boolean'),
+ 'bigint' => array('name' => 'integer'),
+ 'smallint' => array('name' => 'integer'),
+ 'real' => array('name' => 'float'),
+ 'double precision' => array('name' => 'float'),
+ 'numeric' => array('name' => 'float'),
+ 'decimal' => array('name' => 'float')
);
}
+ public function boolean_to_string($value)
+ {
+ if (!$value || in_array(strtolower($value), array('f','false','n','no','off')))
+ return "0";
+ else
+ return "1";
+ }
}
-?>
diff --git a/lib/adapters/SqliteAdapter.php b/lib/adapters/SqliteAdapter.php
index 0917043be..61eb257f2 100644
--- a/lib/adapters/SqliteAdapter.php
+++ b/lib/adapters/SqliteAdapter.php
@@ -110,4 +110,3 @@ public function native_database_types()
}
}
-?>
\ No newline at end of file
diff --git a/test/ActiveRecordCacheTest.php b/test/ActiveRecordCacheTest.php
index 59749d613..6d43cca45 100644
--- a/test/ActiveRecordCacheTest.php
+++ b/test/ActiveRecordCacheTest.php
@@ -41,4 +41,4 @@ public function test_caches_column_meta_data()
$this->assert_true(is_array($value));
}
}
-?>
+
diff --git a/test/ActiveRecordFindTest.php b/test/ActiveRecordFindTest.php
index 329eb9e67..b1e76a8ca 100644
--- a/test/ActiveRecordFindTest.php
+++ b/test/ActiveRecordFindTest.php
@@ -226,12 +226,12 @@ public function test_fetch_all()
public function test_count()
{
- $this->assert_equals(1,Author::count(1));
- $this->assert_equals(2,Author::count(array(1,2)));
+ $this->assert_same(1,Author::count(1));
+ $this->assert_same(2,Author::count(array(1,2)));
$this->assert_true(Author::count() > 1);
- $this->assert_equals(0,Author::count(array('conditions' => 'author_id=99999999999999')));
- $this->assert_equals(2,Author::count(array('conditions' => 'author_id=1 or author_id=2')));
- $this->assert_equals(1,Author::count(array('name' => 'Tito', 'author_id' => 1)));
+ $this->assert_same(0,Author::count(array('conditions' => 'author_id=99999999999999')));
+ $this->assert_same(2,Author::count(array('conditions' => 'author_id=1 or author_id=2')));
+ $this->assert_same(1,Author::count(array('name' => 'Tito', 'author_id' => 1)));
}
public function test_gh149_empty_count()
@@ -476,5 +476,4 @@ public function test_find_by_datetime()
$this->assert_not_null(Author::find_by_created_at($now));
$this->assert_not_null(Author::find_by_created_at($arnow));
}
-};
-?>
+}
diff --git a/test/ActiveRecordTest.php b/test/ActiveRecordTest.php
index f0a0fd110..cc0eaf881 100644
--- a/test/ActiveRecordTest.php
+++ b/test/ActiveRecordTest.php
@@ -167,17 +167,39 @@ public function test_reload_protected_attribute()
public function test_active_record_model_home_not_set()
{
$home = ActiveRecord\Config::instance()->get_model_directory();
- ActiveRecord\Config::instance()->set_model_directory(__FILE__);
+ ActiveRecord\Config::instance()->set_model_directory(__DIR__);
$this->assert_equals(false,class_exists('TestAutoload'));
ActiveRecord\Config::instance()->set_model_directory($home);
}
+ public function test_auto_load_with_model_in_secondary_model_directory(){
+ $home = ActiveRecord\Config::instance()->get_model_directory();
+ ActiveRecord\Config::instance()->set_model_directories(array(
+ realpath(__DIR__ . '/models'),
+ realpath(__DIR__ . '/backup-models'),
+ ));
+ $this->assert_true(class_exists('Backup'));
+
+ ActiveRecord\Config::instance()->set_model_directory($home);
+ }
+
public function test_auto_load_with_namespaced_model()
{
$this->assert_true(class_exists('NamespaceTest\Book'));
}
+ public function test_auto_load_with_namespaced_model_in_secondary_model_directory(){
+ $home = ActiveRecord\Config::instance()->get_model_directory();
+ ActiveRecord\Config::instance()->set_model_directories(array(
+ realpath(__DIR__ . '/models'),
+ realpath(__DIR__ . '/backup-models'),
+ ));
+ $this->assert_true(class_exists('NamespaceTest\Backup'));
+
+ ActiveRecord\Config::instance()->set_model_directory($home);
+ }
+
public function test_namespace_gets_stripped_from_table_name()
{
$model = new NamespaceTest\Book();
@@ -259,6 +281,22 @@ public function test_alias_attribute_setter()
$this->assert_equals($venue->marquee, $venue->name);
}
+ public function test_alias_attribute_custom_setter()
+ {
+ Venue::$use_custom_set_city_setter = true;
+ $venue = Venue::find(1);
+
+ $venue->mycity = 'cityname';
+ $this->assert_equals($venue->mycity, 'cityname#');
+ $this->assert_equals($venue->mycity, $venue->city);
+
+ $venue->city = 'anothercity';
+ $this->assert_equals($venue->city, 'anothercity#');
+ $this->assert_equals($venue->city, $venue->mycity);
+
+ Venue::$use_custom_set_city_setter = false;
+ }
+
public function test_alias_from_mass_attributes()
{
$venue = new Venue(array('marquee' => 'meme', 'id' => 123));
@@ -526,6 +564,102 @@ public function test_gh245_dirty_attribute_should_not_raise_php_notice_if_not_di
$this->assert_true($event->attribute_is_dirty('title'));
}
+ public function test_attribute_is_not_flagged_dirty_if_assigning_same_value() {
+ $event = Event::find(1);
+ $event->type = "Music";
+ $this->assert_false($event->attribute_is_dirty('type'));
+ }
+
+ public function test_changed_attributes() {
+ $event = Event::find(1);
+
+ $event->type = "Groovy Music";
+ $changed_attributes = $event->changed_attributes();
+ $this->assert_true(is_array($changed_attributes));
+ $this->assert_equals(1, count($changed_attributes));
+ $this->assert_true(isset($changed_attributes['type']));
+ $this->assert_equals("Music", $changed_attributes['type']);
+
+ $event->type = "Funky Music";
+ $changed_attributes = $event->changed_attributes();
+ $this->assert_true(is_array($changed_attributes));
+ $this->assert_equals(1, count($changed_attributes));
+ $this->assert_true(isset($changed_attributes['type']));
+ $this->assert_equals("Music", $changed_attributes['type']);
+ }
+
+ public function test_changes() {
+ $event = Event::find(1);
+
+ $event->type = "Groovy Music";
+ $changes = $event->changes();
+ $this->assert_true(is_array($changes));
+ $this->assert_equals(1, count($changes));
+ $this->assert_true(isset($changes['type']));
+ $this->assert_true(is_array($changes['type']));
+ $this->assert_equals("Music", $changes['type'][0]);
+ $this->assert_equals("Groovy Music", $changes['type'][1]);
+
+ $event->type = "Funky Music";
+ $changes = $event->changes();
+ $this->assert_true(is_array($changes));
+ $this->assert_equals(1, count($changes));
+ $this->assert_true(isset($changes['type']));
+ $this->assert_true(is_array($changes['type']));
+ $this->assert_equals("Music", $changes['type'][0]);
+ $this->assert_equals("Funky Music", $changes['type'][1]);
+ }
+
+ public function test_attribute_was() {
+ $event = Event::find(1);
+ $event->type = "Funky Music";
+ $this->assert_equals("Music", $event->attribute_was("type"));
+ $event->type = "Groovy Music";
+ $this->assert_equals("Music", $event->attribute_was("type"));
+ }
+
+ public function test_previous_changes() {
+ $event = Event::find(1);
+ $event->type = "Groovy Music";
+ $previous_changes = $event->previous_changes();
+ $this->assert_true(empty($previous_changes));
+ $event->save();
+ $previous_changes = $event->previous_changes();
+ $this->assert_true(is_array($previous_changes));
+ $this->assert_equals(1, count($previous_changes));
+ $this->assert_true(isset($previous_changes['type']));
+ $this->assert_true(is_array($previous_changes['type']));
+ $this->assert_equals("Music", $previous_changes['type'][0]);
+ $this->assert_equals("Groovy Music", $previous_changes['type'][1]);
+ }
+
+ public function test_save_resets_changed_attributes() {
+ $event = Event::find(1);
+ $event->type = "Groovy Music";
+ $event->save();
+ $changed_attributes = $event->changed_attributes();
+ $this->assert_true(empty($changed_attributes));
+ }
+
+ public function test_changing_datetime_attribute_tracks_change() {
+ $author = new Author();
+ $author->created_at = $original = new \DateTime("yesterday");
+ $author->created_at = $now = new \DateTime();
+ $changes = $author->changes();
+ $this->assert_true(isset($changes['created_at']));
+ $this->assert_datetime_equals($original, $changes['created_at'][0]);
+ $this->assert_datetime_equals($now, $changes['created_at'][1]);
+ }
+
+ public function test_changing_empty_attribute_value_tracks_change() {
+ $event = new Event();
+ $event->description = "The most fun";
+ $changes = $event->changes();
+ $this->assert_true(array_key_exists("description", $changes));
+ $this->assert_equals("", $changes['description'][0]);
+ $this->assert_equals("The most fun", $changes['description'][1]);
+ }
+
public function test_assigning_php_datetime_gets_converted_to_date_class_with_defaults()
{
$author = new Author();
@@ -571,5 +705,4 @@ public function test_query()
$row = Author::query('SELECT COUNT(*) AS n FROM authors WHERE name=?',array('Tito'))->fetch();
$this->assert_equals(array('n' => 1), $row);
}
-};
-?>
+}
diff --git a/test/ActiveRecordWriteTest.php b/test/ActiveRecordWriteTest.php
index 48c156c34..c0351413d 100644
--- a/test/ActiveRecordWriteTest.php
+++ b/test/ActiveRecordWriteTest.php
@@ -10,7 +10,7 @@ public function before_save()
{
$this->name = 'i saved';
}
-};
+}
class AuthorWithoutSequence extends ActiveRecord\Model
{
@@ -441,4 +441,4 @@ public function test_update_our_datetime()
$this->assert_true($our_datetime === $author->some_date);
}
-};
+}
diff --git a/test/CacheTest.php b/test/CacheTest.php
index 3e390a7cd..af5e5426c 100644
--- a/test/CacheTest.php
+++ b/test/CacheTest.php
@@ -88,4 +88,3 @@ public function test_exception_when_connect_fails()
Cache::initialize('memcache://127.0.0.1:1234');
}
}
-?>
diff --git a/test/CallbackTest.php b/test/CallbackTest.php
index 00615b12d..9c1e46d29 100644
--- a/test/CallbackTest.php
+++ b/test/CallbackTest.php
@@ -290,5 +290,4 @@ public function test_before_validation_returned_false_halts_execution()
$this->assert_false($ret);
$this->assert_true(strpos(ActiveRecord\Table::load('VenueCB')->last_sql, 'UPDATE') === false);
}
-};
-?>
\ No newline at end of file
+}
diff --git a/test/ColumnTest.php b/test/ColumnTest.php
index 5330814ea..c36dceeca 100644
--- a/test/ColumnTest.php
+++ b/test/ColumnTest.php
@@ -95,6 +95,9 @@ public function test_cast()
// 64 bit
elseif (PHP_INT_SIZE === 8)
$this->assert_cast(Column::INTEGER,'9223372036854775808',(((float) PHP_INT_MAX) + 1));
+
+ $this->assert_cast(Column::BOOLEAN,$this->conn->boolean_to_string(true),true);
+ $this->assert_cast(Column::BOOLEAN,$this->conn->boolean_to_string(false),false);
}
public function test_cast_leave_null_alone()
@@ -104,7 +107,8 @@ public function test_cast_leave_null_alone()
Column::INTEGER,
Column::DECIMAL,
Column::DATETIME,
- Column::DATE);
+ Column::DATE,
+ Column::BOOLEAN);
foreach ($types as $type) {
$this->assert_cast($type,null,null);
@@ -155,4 +159,3 @@ public function test_ar_date_time_attribute_copies_exact_tz()
$this->assert_equals($dt->getTimeZone()->getName(), $dt2->getTimeZone()->getName());
}
}
-?>
diff --git a/test/ConfigTest.php b/test/ConfigTest.php
index f547d5197..f15510a27 100644
--- a/test/ConfigTest.php
+++ b/test/ConfigTest.php
@@ -24,7 +24,14 @@ class ConfigTest extends SnakeCase_PHPUnit_Framework_TestCase
public function set_up()
{
$this->config = new Config();
- $this->connections = array('development' => 'mysql://blah/development', 'test' => 'mysql://blah/test');
+ $this->connections = array(
+ 'development' => 'mysql://blah/development',
+ 'test' => 'mysql://blah/test',
+ 'production' => array(
+ 'adapter' => 'mysql',
+ 'host' => '127.0.0.1',
+ 'database' => 'production'
+ ));
$this->config->set_connections($this->connections);
}
@@ -38,36 +45,40 @@ public function test_set_connections_must_be_array()
public function test_get_connections()
{
- $this->assert_equals($this->connections,$this->config->get_connections());
+ $connections = $this->config->get_connections();
+ $this->assert_instance_of('ActiveRecord\ConnectionInfo', $connections['development']);
+ $this->assert_equals('development', $connections['development']->database);
}
- public function test_get_connection()
+ public function test_get_connection_info()
{
- $this->assert_equals($this->connections['development'],$this->config->get_connection('development'));
+ $connection = $this->config->get_connection_info('development');
+ $this->assert_instance_of('ActiveRecord\ConnectionInfo', $connection);
+ $this->assert_equals('development', $connection->database);
}
public function test_get_invalid_connection()
{
- $this->assert_null($this->config->get_connection('whiskey tango foxtrot'));
+ $this->assert_null($this->config->get_connection_info('whiskey tango foxtrot'));
}
public function test_get_default_connection_and_connection()
{
- $this->config->set_default_connection('development');
- $this->assert_equals('development',$this->config->get_default_connection());
- $this->assert_equals($this->connections['development'],$this->config->get_default_connection_string());
+ $this->config->set_default_connection('test');
+ $this->assert_equals('test', $this->config->get_default_connection());
+ $this->assert_equals('test', $this->config->get_default_connection_info()->database);
}
- public function test_get_default_connection_and_connection_string_defaults_to_development()
+ public function test_get_default_connection_and_connection_info_defaults_to_development()
{
- $this->assert_equals('development',$this->config->get_default_connection());
- $this->assert_equals($this->connections['development'],$this->config->get_default_connection_string());
+ $this->assert_equals('development', $this->config->get_default_connection());
+ $this->assert_equals('development', $this->config->get_default_connection_info()->database);
}
- public function test_get_default_connection_string_when_connection_name_is_not_valid()
+ public function test_get_default_connection_info_when_connection_name_is_not_valid()
{
$this->config->set_default_connection('little mac');
- $this->assert_null($this->config->get_default_connection_string());
+ $this->assert_null($this->config->get_default_connection_info());
}
public function test_default_connection_is_set_when_only_one_connection_is_present()
@@ -82,6 +93,14 @@ public function test_set_connections_with_default()
$this->assert_equals('test',$this->config->get_default_connection());
}
+ /**
+ * @expectedException ActiveRecord\ConfigException
+ */
+ public function test_set_model_directories_must_be_array()
+ {
+ $this->config->set_model_directories(null);
+ }
+
public function test_get_date_class_with_default()
{
$this->assert_equals('ActiveRecord\\DateTime', $this->config->get_date_class());
@@ -95,6 +114,41 @@ public function test_set_date_class_when_class_doesnt_exist()
$this->config->set_date_class('doesntexist');
}
+ /**
+ * @expectedException ActiveRecord\ConfigException
+ */
+ public function test_set_model_directories_directories_must_exist(){
+ $home = ActiveRecord\Config::instance()->get_model_directory();
+
+ $this->config->set_model_directories(array(
+ realpath(__DIR__ . '/models'),
+ '/some-non-existing-directory'
+ ));
+
+ ActiveRecord\Config::instance()->set_model_directory($home);
+ }
+
+ public function test_set_model_directory_stores_as_array(){
+ $home = ActiveRecord\Config::instance()->get_model_directory();
+
+ $this->config->set_model_directory(realpath(__DIR__ . '/models'));
+ $this->assertInternalType('array', $this->config->get_model_directories());
+
+ ActiveRecord\Config::instance()->set_model_directory($home);
+ }
+
+ public function test_get_model_directory_returns_first_model_directory(){
+ $home = ActiveRecord\Config::instance()->get_model_directory();
+
+ $this->config->set_model_directories(array(
+ realpath(__DIR__ . '/models'),
+ realpath(__DIR__ . '/backup-models'),
+ ));
+ $this->assert_equals(realpath(__DIR__ . '/models'), $this->config->get_model_directory());
+
+ ActiveRecord\Config::instance()->set_model_directory($home);
+ }
+
/**
* @expectedException ActiveRecord\ConfigException
*/
@@ -138,4 +192,3 @@ public function test_logger_object_must_implement_log_method()
}
}
}
-?>
diff --git a/test/ConnectionInfoTest.php b/test/ConnectionInfoTest.php
new file mode 100644
index 000000000..cea868509
--- /dev/null
+++ b/test/ConnectionInfoTest.php
@@ -0,0 +1,95 @@
+assert_equals('mysql', $info->adapter);
+ $this->assert_equals('user', $info->username);
+ $this->assert_equals('pass', $info->password);
+ $this->assert_equals('127.0.0.1', $info->host);
+ $this->assert_equals(3306, $info->port);
+ $this->assert_equals('dbname', $info->database);
+ }
+
+ public function test_gh_103_sqlite_connection_string_relative()
+ {
+ $info = ConnectionInfo::from_connection_url('sqlite://../some/path/to/file.db');
+ $this->assert_equals('../some/path/to/file.db', $info->host);
+ }
+
+ /**
+ * @expectedException ActiveRecord\DatabaseException
+ */
+ public function test_gh_103_sqlite_connection_string_absolute()
+ {
+ $info = ConnectionInfo::from_connection_url('sqlite:///some/path/to/file.db');
+ }
+
+ public function test_gh_103_sqlite_connection_string_unix()
+ {
+ $info = ConnectionInfo::from_connection_url('sqlite://unix(/some/path/to/file.db)');
+ $this->assert_equals('/some/path/to/file.db', $info->host);
+
+ $info = ConnectionInfo::from_connection_url('sqlite://unix(/some/path/to/file.db)/');
+ $this->assert_equals('/some/path/to/file.db', $info->host);
+
+ $info = ConnectionInfo::from_connection_url('sqlite://unix(/some/path/to/file.db)/dummy');
+ $this->assert_equals('/some/path/to/file.db', $info->host);
+ }
+
+ public function test_gh_103_sqlite_connection_string_windows()
+ {
+ $info = ConnectionInfo::from_connection_url('sqlite://windows(c%3A/some/path/to/file.db)');
+ $this->assert_equals('c:/some/path/to/file.db', $info->host);
+ }
+
+ public function test_parse_connection_url_with_unix_sockets()
+ {
+ $info = ConnectionInfo::from_connection_url('mysql://user:password@unix(/tmp/mysql.sock)/database');
+ $this->assert_equals('/tmp/mysql.sock', $info->host);
+ $this->assert_equals('database', $info->database);
+ }
+
+ public function test_parse_connection_url_with_decode_option()
+ {
+ $info = ConnectionInfo::from_connection_url('mysql://h%20az:h%40i@127.0.0.1/test?decode=true');
+ $this->assert_equals('h az', $info->username);
+ $this->assert_equals('h@i', $info->password);
+ }
+
+ public function test_encoding()
+ {
+ $info = ConnectionInfo::from_connection_url('mysql://test:test@127.0.0.1/test?charset=utf8');
+ $this->assert_equals('utf8', $info->charset);
+ }
+
+ public function test_connection_info_from_array(){
+ $info = new ConnectionInfo(array(
+ 'adapter' => 'mysql',
+ 'host' => '127.0.0.1',
+ 'port' => 3306,
+ 'database' => 'dbname',
+ 'username' => 'user',
+ 'password' => 'pass'
+ ));
+ $this->assert_equals('mysql', $info->adapter);
+ $this->assert_equals('user', $info->username);
+ $this->assert_equals('pass', $info->password);
+ $this->assert_equals('127.0.0.1', $info->host);
+ $this->assert_equals(3306, $info->port);
+ $this->assert_equals('dbname', $info->database);
+ }
+
+}
diff --git a/test/ConnectionTest.php b/test/ConnectionTest.php
deleted file mode 100644
index 59f91c7c6..000000000
--- a/test/ConnectionTest.php
+++ /dev/null
@@ -1,80 +0,0 @@
-assert_equals('mysql',$info->protocol);
- $this->assert_equals('user',$info->user);
- $this->assert_equals('pass',$info->pass);
- $this->assert_equals('127.0.0.1',$info->host);
- $this->assert_equals(3306,$info->port);
- $this->assert_equals('dbname',$info->db);
- }
-
- public function test_gh_103_sqlite_connection_string_relative()
- {
- $info = ActiveRecord\Connection::parse_connection_url('sqlite://../some/path/to/file.db');
- $this->assert_equals('../some/path/to/file.db', $info->host);
- }
-
- /**
- * @expectedException ActiveRecord\DatabaseException
- */
- public function test_gh_103_sqlite_connection_string_absolute()
- {
- $info = ActiveRecord\Connection::parse_connection_url('sqlite:///some/path/to/file.db');
- }
-
- public function test_gh_103_sqlite_connection_string_unix()
- {
- $info = ActiveRecord\Connection::parse_connection_url('sqlite://unix(/some/path/to/file.db)');
- $this->assert_equals('/some/path/to/file.db', $info->host);
-
- $info = ActiveRecord\Connection::parse_connection_url('sqlite://unix(/some/path/to/file.db)/');
- $this->assert_equals('/some/path/to/file.db', $info->host);
-
- $info = ActiveRecord\Connection::parse_connection_url('sqlite://unix(/some/path/to/file.db)/dummy');
- $this->assert_equals('/some/path/to/file.db', $info->host);
- }
-
- public function test_gh_103_sqlite_connection_string_windows()
- {
- $info = ActiveRecord\Connection::parse_connection_url('sqlite://windows(c%3A/some/path/to/file.db)');
- $this->assert_equals('c:/some/path/to/file.db', $info->host);
- }
-
- public function test_parse_connection_url_with_unix_sockets()
- {
- $info = ActiveRecord\Connection::parse_connection_url('mysql://user:password@unix(/tmp/mysql.sock)/database');
- $this->assert_equals('/tmp/mysql.sock',$info->host);
- }
-
- public function test_parse_connection_url_with_decode_option()
- {
- $info = ActiveRecord\Connection::parse_connection_url('mysql://h%20az:h%40i@127.0.0.1/test?decode=true');
- $this->assert_equals('h az',$info->user);
- $this->assert_equals('h@i',$info->pass);
- }
-
- public function test_encoding()
- {
- $info = ActiveRecord\Connection::parse_connection_url('mysql://test:test@127.0.0.1/test?charset=utf8');
- $this->assert_equals('utf8', $info->charset);
- }
-}
-?>
diff --git a/test/DateFormatTest.php b/test/DateFormatTest.php
index ab857e90a..b18faffd7 100644
--- a/test/DateFormatTest.php
+++ b/test/DateFormatTest.php
@@ -14,5 +14,4 @@ public function test_datefield_gets_converted_to_ar_datetime()
$this->assert_is_a("ActiveRecord\\DateTime",$author->some_date);
}
-};
-?>
+}
diff --git a/test/DateTimeTest.php b/test/DateTimeTest.php
index 882328e5a..14839da16 100644
--- a/test/DateTimeTest.php
+++ b/test/DateTimeTest.php
@@ -224,4 +224,3 @@ public function test_clone()
$this->assert_not_same($datetime, $cloned_datetime);
}
}
-?>
diff --git a/test/ExpressionsTest.php b/test/ExpressionsTest.php
index fdddb57ea..c1d90aab3 100644
--- a/test/ExpressionsTest.php
+++ b/test/ExpressionsTest.php
@@ -1,5 +1,4 @@
assert_equals('id=? AND name IN(?,?)',$a->to_s());
}
}
-?>
diff --git a/test/HasManyThroughTest.php b/test/HasManyThroughTest.php
index ff9586cc1..1dcbdf3c3 100644
--- a/test/HasManyThroughTest.php
+++ b/test/HasManyThroughTest.php
@@ -55,6 +55,41 @@ public function test_gh107_has_many_though_include_eager_with_namespace()
$this->assert_equals(1, $user->id);
$this->assert_equals(1, $user->newsletters[0]->id);
}
+
+ public function test_gh68_has_many_through_with_missing_associations()
+ {
+ Property::$has_many = array(
+ array('amenities', 'through' => 'property_amenities')
+ );
+ Amenity::$has_many = array(
+ array('properties', 'through' => 'property_amenities')
+ );
+
+ $property = Property::find('first');
+ try{
+ $property->amenities;
+ $this->fail('Should trigger exception');
+ }catch(ActiveRecord\HasManyThroughAssociationException $e){
+ $this->assertEquals('Could not find the association property_amenities in model Property', $e->getMessage());
+ }
+ }
+
+ public function test_gh68_has_many_through_with_missing_association()
+ {
+ Property::$has_many = array(
+ 'property_amenities',
+ array('amenities', 'through' => 'property_amenities')
+ );
+ Amenity::$has_many = array(
+ array('properties', 'through' => 'property_amenities')
+ );
+
+ $amenity = Amenity::find('first');
+ try{
+ $amenity->properties;
+ $this->fail('Should trigger exception');
+ }catch(ActiveRecord\HasManyThroughAssociationException $e){
+ $this->assertEquals('Could not find the association property_amenities in model Amenity', $e->getMessage());
+ }
+ }
}
-# vim: noet ts=4 nobinary
-?>
diff --git a/test/InflectorTest.php b/test/InflectorTest.php
index 7726592f0..d93f40b38 100644
--- a/test/InflectorTest.php
+++ b/test/InflectorTest.php
@@ -24,5 +24,4 @@ public function test_keyify()
{
$this->assert_equals('building_type_id', $this->inflector->keyify('BuildingType'));
}
-};
-?>
\ No newline at end of file
+}
diff --git a/test/ModelCallbackTest.php b/test/ModelCallbackTest.php
index 949706385..b9ffd7466 100644
--- a/test/ModelCallbackTest.php
+++ b/test/ModelCallbackTest.php
@@ -94,4 +94,3 @@ public function test_destroy()
function($model) { $model->delete(); });
}
}
-?>
diff --git a/test/MysqlAdapterTest.php b/test/MysqlAdapterTest.php
index 2a93570e6..f7153eea8 100644
--- a/test/MysqlAdapterTest.php
+++ b/test/MysqlAdapterTest.php
@@ -20,9 +20,10 @@ public function test_enum()
public function test_set_charset()
{
- $connection_string = ActiveRecord\Config::instance()->get_connection($this->connection_name);
- $conn = ActiveRecord\Connection::instance($connection_string . '?charset=utf8');
- $this->assert_equals('SET NAMES ?',$conn->last_query);
+ $connection_info = ActiveRecord\Config::instance()->get_connection_info($this->connection_name);
+ $connection_info->charset = 'utf8';
+ $conn = ActiveRecord\Connection::instance($connection_info);
+ $this->assert_equals('SET NAMES ?', $conn->last_query);
}
public function test_limit_with_null_offset_does_not_contain_offset()
@@ -34,4 +35,3 @@ public function test_limit_with_null_offset_does_not_contain_offset()
$this->assert_true(strpos($this->conn->last_query, 'LIMIT 1') !== false);
}
}
-?>
diff --git a/test/OciAdapterTest.php b/test/OciAdapterTest.php
index d820c0fd5..2a8fa7376 100644
--- a/test/OciAdapterTest.php
+++ b/test/OciAdapterTest.php
@@ -38,9 +38,9 @@ public function test_columns_sequence() {}
public function test_set_charset()
{
- $connection_string = ActiveRecord\Config::instance()->get_connection($this->connection_name);
- $conn = ActiveRecord\Connection::instance($connection_string . '?charset=utf8');
+ $connection_info = ActiveRecord\Config::instance()->get_connection_info($this->connection_name);
+ $connection_info->charset = 'utf8';
+ $conn = ActiveRecord\Connection::instance($connection_info);
$this->assert_equals(';charset=utf8', $conn->dsn_params);
}
}
-?>
diff --git a/test/PgsqlAdapterTest.php b/test/PgsqlAdapterTest.php
index 348b11e9f..c7d23a594 100644
--- a/test/PgsqlAdapterTest.php
+++ b/test/PgsqlAdapterTest.php
@@ -31,8 +31,9 @@ public function test_insert_id_should_return_explicitly_inserted_id()
public function test_set_charset()
{
- $connection_string = ActiveRecord\Config::instance()->get_connection($this->connection_name);
- $conn = ActiveRecord\Connection::instance($connection_string . '?charset=utf8');
+ $connection_info = ActiveRecord\Config::instance()->get_connection_info($this->connection_name);
+ $connection_info->charset = 'utf8';
+ $conn = ActiveRecord\Connection::instance($connection_info);
$this->assert_equals("SET NAMES 'utf8'",$conn->last_query);
}
@@ -40,5 +41,24 @@ public function test_gh96_columns_not_duplicated_by_index()
{
$this->assert_equals(3,$this->conn->query_column_info("user_newsletters")->rowCount());
}
+
+ public function test_boolean_to_string()
+ {
+ // false values
+ $this->assert_equals("0", $this->conn->boolean_to_string(false));
+ $this->assert_equals("0", $this->conn->boolean_to_string('0'));
+ $this->assert_equals("0", $this->conn->boolean_to_string('f'));
+ $this->assert_equals("0", $this->conn->boolean_to_string('false'));
+ $this->assert_equals("0", $this->conn->boolean_to_string('n'));
+ $this->assert_equals("0", $this->conn->boolean_to_string('no'));
+ $this->assert_equals("0", $this->conn->boolean_to_string('off'));
+ // true values
+ $this->assert_equals("1", $this->conn->boolean_to_string(true));
+ $this->assert_equals("1", $this->conn->boolean_to_string('1'));
+ $this->assert_equals("1", $this->conn->boolean_to_string('t'));
+ $this->assert_equals("1", $this->conn->boolean_to_string('true'));
+ $this->assert_equals("1", $this->conn->boolean_to_string('y'));
+ $this->assert_equals("1", $this->conn->boolean_to_string('yes'));
+ $this->assert_equals("1", $this->conn->boolean_to_string('on'));
+ }
}
-?>
diff --git a/test/RelationshipTest.php b/test/RelationshipTest.php
index d8b99b090..6adce031d 100644
--- a/test/RelationshipTest.php
+++ b/test/RelationshipTest.php
@@ -1,6 +1,6 @@
'property_amenities')
+ );
+ Amenity::$has_many = array(
+ 'property_amenities'
+ );
+
$property = Property::first();
$this->assert_equals(1, $property->amenities[0]->amenity_id);
@@ -548,7 +556,7 @@ public function test_gh93_and_gh100_eager_loading_respects_association_options()
Venue::$has_many = array(array('events', 'class_name' => 'Event', 'order' => 'id asc', 'conditions' => array('length(title) = ?', 14)));
$venues = Venue::find(array(2, 6), array('include' => 'events'));
- $this->assert_sql_has("WHERE length(title) = ? AND venue_id IN(?,?) ORDER BY id asc",ActiveRecord\Table::load('Event')->last_sql);
+ $this->assert_sql_has("WHERE (length(title) = ?) AND (venue_id IN(?,?)) ORDER BY id asc",ActiveRecord\Table::load('Event')->last_sql);
$this->assert_equals(1, count($venues[0]->events));
}
@@ -740,5 +748,4 @@ public function test_dont_attempt_eager_load_when_record_does_not_exist()
{
Author::find(999999, array('include' => array('books')));
}
-};
-?>
+}
diff --git a/test/SQLBuilderTest.php b/test/SQLBuilderTest.php
index 603b60b6b..b44ca1c41 100644
--- a/test/SQLBuilderTest.php
+++ b/test/SQLBuilderTest.php
@@ -278,5 +278,4 @@ public function test_where_with_joins_prepends_table_name_to_fields()
$this->assert_sql_has("SELECT * FROM authors $joins WHERE authors.id=? AND authors.name=?",(string)$this->sql);
}
-};
-?>
\ No newline at end of file
+}
diff --git a/test/SerializationTest.php b/test/SerializationTest.php
index e8ef9cebd..562e24d39 100644
--- a/test/SerializationTest.php
+++ b/test/SerializationTest.php
@@ -64,14 +64,21 @@ public function test_methods_takes_a_string()
$this->assert_equals('ANCIENT ART OF MAIN TANKING', $a['upper_name']);
}
- // methods added last should we shuld have value of the method in our json
- // rather than the regular attribute value
+ // methods should take precedence over attributes
public function test_methods_method_same_as_attribute()
{
$a = $this->_a(array('methods' => 'name'));
$this->assert_equals('ancient art of main tanking', $a['name']);
}
+ public function test_methods_method_alias()
+ {
+ $a = $this->_a(array('methods' => array('name' => 'alias_name')));
+ $this->assert_equals('ancient art of main tanking', $a['alias_name']);
+ $a = $this->_a(array('methods' => array('upper_name' => 'name')));
+ $this->assert_equals('ANCIENT ART OF MAIN TANKING', $a['name']);
+ }
+
public function test_include()
{
$a = $this->_a(array('include' => array('author')));
@@ -123,30 +130,30 @@ public function test_to_xml()
$this->assert_equals($book->attributes(),get_object_vars(new SimpleXMLElement($book->to_xml())));
}
- public function test_to_array()
- {
- $book = Book::find(1);
+ public function test_to_array()
+ {
+ $book = Book::find(1);
$array = $book->to_array();
$this->assert_equals($book->attributes(), $array);
- }
+ }
- public function test_to_array_include_root()
- {
+ public function test_to_array_include_root()
+ {
ActiveRecord\ArraySerializer::$include_root = true;
- $book = Book::find(1);
+ $book = Book::find(1);
$array = $book->to_array();
- $book_attributes = array('book' => $book->attributes());
+ $book_attributes = array('book' => $book->attributes());
$this->assert_equals($book_attributes, $array);
- }
+ }
- public function test_to_array_except()
- {
- $book = Book::find(1);
+ public function test_to_array_except()
+ {
+ $book = Book::find(1);
$array = $book->to_array(array('except' => array('special')));
$book_attributes = $book->attributes();
unset($book_attributes['special']);
$this->assert_equals($book_attributes, $array);
- }
+ }
public function test_works_with_datetime()
{
@@ -166,50 +173,49 @@ public function test_only_method()
$this->assert_contains('lasers', Author::first()->to_xml(array('only_method' => 'return_something')));
}
- public function test_to_csv()
- {
- $book = Book::find(1);
- $this->assert_equals('1,1,2,"Ancient Art of Main Tanking",0,0',$book->to_csv());
- }
-
- public function test_to_csv_only_header()
- {
- $book = Book::find(1);
- $this->assert_equals('book_id,author_id,secondary_author_id,name,numeric_test,special',
- $book->to_csv(array('only_header'=>true))
- );
- }
-
- public function test_to_csv_only_method()
- {
- $book = Book::find(1);
- $this->assert_equals('2,"Ancient Art of Main Tanking"',
- $book->to_csv(array('only'=>array('name','secondary_author_id')))
- );
- }
-
- public function test_to_csv_only_method_on_header()
- {
- $book = Book::find(1);
- $this->assert_equals('secondary_author_id,name',
- $book->to_csv(array('only'=>array('secondary_author_id','name'),
- 'only_header'=>true))
- );
- }
-
- public function test_to_csv_with_custom_delimiter()
- {
- $book = Book::find(1);
- ActiveRecord\CsvSerializer::$delimiter=';';
- $this->assert_equals('1;1;2;"Ancient Art of Main Tanking";0;0',$book->to_csv());
- }
-
- public function test_to_csv_with_custom_enclosure()
- {
- $book = Book::find(1);
- ActiveRecord\CsvSerializer::$delimiter=',';
- ActiveRecord\CsvSerializer::$enclosure="'";
- $this->assert_equals("1,1,2,'Ancient Art of Main Tanking',0,0",$book->to_csv());
- }
-};
-?>
+ public function test_to_csv()
+ {
+ $book = Book::find(1);
+ $this->assert_equals('1,1,2,"Ancient Art of Main Tanking",0,0',$book->to_csv());
+ }
+
+ public function test_to_csv_only_header()
+ {
+ $book = Book::find(1);
+ $this->assert_equals('book_id,author_id,secondary_author_id,name,numeric_test,special',
+ $book->to_csv(array('only_header'=>true))
+ );
+ }
+
+ public function test_to_csv_only_method()
+ {
+ $book = Book::find(1);
+ $this->assert_equals('2,"Ancient Art of Main Tanking"',
+ $book->to_csv(array('only'=>array('name','secondary_author_id')))
+ );
+ }
+
+ public function test_to_csv_only_method_on_header()
+ {
+ $book = Book::find(1);
+ $this->assert_equals('secondary_author_id,name',
+ $book->to_csv(array('only'=>array('secondary_author_id','name'),
+ 'only_header'=>true))
+ );
+ }
+
+ public function test_to_csv_with_custom_delimiter()
+ {
+ $book = Book::find(1);
+ ActiveRecord\CsvSerializer::$delimiter=';';
+ $this->assert_equals('1;1;2;"Ancient Art of Main Tanking";0;0',$book->to_csv());
+ }
+
+ public function test_to_csv_with_custom_enclosure()
+ {
+ $book = Book::find(1);
+ ActiveRecord\CsvSerializer::$delimiter=',';
+ ActiveRecord\CsvSerializer::$enclosure="'";
+ $this->assert_equals("1,1,2,'Ancient Art of Main Tanking',0,0",$book->to_csv());
+ }
+}
diff --git a/test/SqliteAdapterTest.php b/test/SqliteAdapterTest.php
index 0490c8137..146152cf8 100644
--- a/test/SqliteAdapterTest.php
+++ b/test/SqliteAdapterTest.php
@@ -79,4 +79,3 @@ public function test_date_to_string()
// not supported
public function test_connect_with_port() {}
}
-?>
\ No newline at end of file
diff --git a/test/StrongParametersModelTest.php b/test/StrongParametersModelTest.php
new file mode 100644
index 000000000..b2972cca4
--- /dev/null
+++ b/test/StrongParametersModelTest.php
@@ -0,0 +1,80 @@
+get_require_strong_parameters();
+ $config->set_require_strong_parameters(true);
+ }
+
+ public static function tearDownAfterClass()
+ {
+ $config = Config::instance();
+ $config->set_require_strong_parameters(self::$require_strong_parameters);
+ }
+
+ public function testConstructWithStrongParameters()
+ {
+ $params = new StrongParameters(array(
+ 'name' => 'Foo',
+ ));
+ $book = new Book($params);
+ $this->assertNull($book->name);
+
+ $params->permit('name');
+ $book = new Book($params);
+ $this->assertEquals('Foo', $book->name);
+ }
+
+ /**
+ * @expectedException ActiveRecord\UnsafeParametersException
+ */
+ public function testConstructWithoutStrongParametersThrowsException()
+ {
+ $config = Config::instance();
+ $require_strong_parameters = $config->get_require_strong_parameters();
+ $config->set_require_strong_parameters(true);
+
+ $params = array('name' => 'Foo');
+ $book = new Book($params);
+ }
+
+ public function testUpdateAttributesWithStrongParameters()
+ {
+ $params = new StrongParameters(array(
+ 'name' => 'Foo',
+ ));
+ $book = new Book();
+ $book->update_attributes($params);
+ $this->assertNull($book->name);
+
+ $params->permit('name');
+ $book->update_attributes($params);
+ $this->assertEquals('Foo', $book->name);
+ }
+
+ /**
+ * @expectedException ActiveRecord\UnsafeParametersException
+ */
+ public function testUpdateAttributesWithoutStrongParameters()
+ {
+ $config = Config::instance();
+ $require_strong_parameters = $config->get_require_strong_parameters();
+ $config->set_require_strong_parameters(true);
+
+ $params = array('name' => 'Foo');
+ $book = new Book();
+ $book->update_attributes($params);
+ }
+
+
+}
diff --git a/test/StrongParametersTest.php b/test/StrongParametersTest.php
new file mode 100644
index 000000000..750ba6c60
--- /dev/null
+++ b/test/StrongParametersTest.php
@@ -0,0 +1,158 @@
+params = new StrongParameters(array(
+ 'user' => array(
+ 'name' => 'Foo Bar',
+ 'email' => 'foo@bar.baz',
+ 'bio' => 'I am Foo Bar',
+ 'is_admin' => true,
+ ),
+ ));
+ }
+
+ public function testConstruct()
+ {
+ $params = new StrongParameters(array('name' => 'Foo Bar'));
+ $this->assertInstanceOf('ActiveRecord\StrongParameters', $params);
+ }
+
+ public function testPermit()
+ {
+ $params = new StrongParameters(array(
+ 'name' => 'Foo Bar',
+ 'email' => 'foo@bar.baz',
+ 'bio' => 'I am Foo Bar',
+ 'is_admin' => true,
+ ));
+ $params->permit();
+ $expected = array();
+ $actual = iterator_to_array($params);
+ $this->assertEquals($expected, $actual);
+
+ $params->permit(array('name', 'email'));
+ $expected = array('name' => 'Foo Bar', 'email' => 'foo@bar.baz');
+ $actual = iterator_to_array($params);
+ $this->assertEquals($expected, $actual);
+
+ $params->permit('name', 'bio');
+ $expected = array('name' => 'Foo Bar', 'bio' => 'I am Foo Bar');
+ $actual = iterator_to_array($params);
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testPermitReturnsSelf()
+ {
+ $params = new StrongParameters(array(
+ 'name' => 'Foo Bar',
+ 'email' => 'foo@bar.baz',
+ 'bio' => 'I am Foo Bar',
+ 'is_admin' => true,
+ ));
+
+ $expected = array('name' => 'Foo Bar');
+ $actual = iterator_to_array($params->permit('name'));
+ $this->assertEquals($expected, $actual);
+
+ $expected = array('name' => 'Foo Bar', 'bio' => 'I am Foo Bar');
+ $actual = iterator_to_array($params->permit('name', 'bio'));
+ $this->assertEquals($expected, $actual);
+ }
+
+ public function testRequireParam()
+ {
+ $this->assertInstanceOf('ActiveRecord\StrongParameters', $this->params->requireParam('user'));
+ }
+
+ /**
+ * @expectedException ActiveRecord\ParameterMissingException
+ */
+ public function testRequireParamThrowsOnMissingParam()
+ {
+ $this->params->requireParam('nonexisting');
+ }
+
+ public function testFetch()
+ {
+ $params = new StrongParameters(array('name' => 'Foo Bar'));
+ $this->assertEquals('Foo Bar', $params->fetch('name'));
+
+ $this->assertInstanceOf('ActiveRecord\StrongParameters', $this->params->fetch('user'));
+
+ $this->assertEquals('Foo Bar', $this->params->fetch('user')->fetch('name'));
+ }
+
+ public function testGetIterator()
+ {
+ $this->assertInstanceOf('ArrayIterator', $this->params->getIterator());
+ }
+
+ public function testArrayAccessOffsetExists()
+ {
+ $params = new StrongParameters(array('name' => 'Foo Bar'));
+ $this->assertTrue(isset($params['name']));
+ $this->assertFalse(isset($params['undefined']));
+ }
+
+ public function testArrayAccessOffsetGet()
+ {
+ $params = new StrongParameters(array('name' => 'Foo Bar'));
+ $this->assertEquals('Foo Bar', $params['name']);
+
+ $this->assertEquals('Foo Bar', $this->params['user']->fetch('name'));
+ $this->assertEquals('Foo Bar', $this->params['user']['name']);
+ }
+
+ public function testArrayAccessOffsetSet()
+ {
+ $params = new StrongParameters(array('name' => 'Foo Bar'));
+ $params['test'] = 'foobar';
+ $params->permit('test', 'name');
+ $this->assertEquals(array('name' => 'Foo Bar', 'test' => 'foobar'), iterator_to_array($params));
+ }
+
+ /**
+ * @expectedException InvalidArgumentException
+ * @expectedExceptionMessage offset cannot be null
+ */
+ public function testArrayAccessOffsetSetThrowsOnNull()
+ {
+ $params = new StrongParameters(array('name' => 'Foo Bar'));
+ $params[] = 'foobar';
+ }
+
+ public function testArrayAccessOffsetUnset()
+ {
+ $params = new StrongParameters(array('name' => 'Foo Bar'));
+ $params['test'] = 'foobar';
+ $params->permit('test', 'name');
+ $this->assertEquals(array('name' => 'Foo Bar', 'test' => 'foobar'), iterator_to_array($params));
+ unset($params['test']);
+ $this->assertEquals(array('name' => 'Foo Bar'), iterator_to_array($params));
+ unset($params['name']);
+ $this->assertEquals(array(), iterator_to_array($params));
+ }
+
+ public function testArrayAccessOffsetUnsetNested()
+ {
+ $user = $this->params->fetch('user');
+ $user->permit('name', 'email');
+
+ $this->assertEquals(array(
+ 'name' => 'Foo Bar',
+ 'email' => 'foo@bar.baz',
+ ), iterator_to_array($user));
+
+ unset($this->params['user']['name']);
+ $this->assertEquals(array(
+ 'email' => 'foo@bar.baz',
+ ), iterator_to_array($user));
+ }
+
+}
+
diff --git a/test/UtilsTest.php b/test/UtilsTest.php
index 9205a6b8e..37d8bf151 100644
--- a/test/UtilsTest.php
+++ b/test/UtilsTest.php
@@ -103,5 +103,19 @@ public function test_wrap_strings_in_arrays()
$x = '1';
$this->assert_equals(array(array('1')),ActiveRecord\wrap_strings_in_arrays($x));
}
+
+ public function test_is_hash()
+ {
+ $hash = array('key' => 'value');
+ $this->assert_true(ActiveRecord\is_hash($hash));
+
+ $notHash = array(0 => 'value');
+ $this->assert_false(ActiveRecord\is_hash($notHash));
+ }
+
+ public function test_is_hash_empty_array()
+ {
+ $notHash = array();
+ $this->assert_false(ActiveRecord\is_hash($notHash));
+ }
};
-?>
diff --git a/test/ValidatesFormatOfTest.php b/test/ValidatesFormatOfTest.php
index 76c52d5ee..0aaac2f34 100644
--- a/test/ValidatesFormatOfTest.php
+++ b/test/ValidatesFormatOfTest.php
@@ -1,111 +1,110 @@
- 1, 'name' => 'testing reg'));
- $book->save();
- $this->assert_false($book->errors->is_invalid('name'));
-
- BookFormat::$validates_format_of[0]['with'] = '/[0-9]/';
- $book = new BookFormat(array('author_id' => 1, 'name' => 12));
- $book->save();
- $this->assert_false($book->errors->is_invalid('name'));
- }
-
- public function test_invalid_null()
- {
- BookFormat::$validates_format_of[0]['with'] = '/[^0-9]/';
- $book = new BookFormat;
- $book->name = null;
- $book->save();
- $this->assert_true($book->errors->is_invalid('name'));
- }
-
- public function test_invalid_blank()
- {
- BookFormat::$validates_format_of[0]['with'] = '/[^0-9]/';
- $book = new BookFormat;
- $book->name = '';
- $book->save();
- $this->assert_true($book->errors->is_invalid('name'));
- }
-
- public function test_valid_blank_andallow_blank()
- {
- BookFormat::$validates_format_of[0]['allow_blank'] = true;
- BookFormat::$validates_format_of[0]['with'] = '/[^0-9]/';
- $book = new BookFormat(array('author_id' => 1, 'name' => ''));
- $book->save();
- $this->assert_false($book->errors->is_invalid('name'));
- }
-
- public function test_valid_null_and_allow_null()
- {
- BookFormat::$validates_format_of[0]['allow_null'] = true;
- BookFormat::$validates_format_of[0]['with'] = '/[^0-9]/';
- $book = new BookFormat();
- $book->author_id = 1;
- $book->name = null;
- $book->save();
- $this->assert_false($book->errors->is_invalid('name'));
- }
-
- /**
- * @expectedException ActiveRecord\ValidationsArgumentError
- */
- public function test_invalid_lack_of_with_key()
- {
- $book = new BookFormat;
- $book->name = null;
- $book->save();
- }
-
- /**
- * @expectedException ActiveRecord\ValidationsArgumentError
- */
- public function test_invalid_with_expression_as_non_string()
- {
- BookFormat::$validates_format_of[0]['with'] = array('test');
- $book = new BookFormat;
- $book->name = null;
- $book->save();
- }
-
- public function test_invalid_with_expression_as_non_regexp()
- {
- BookFormat::$validates_format_of[0]['with'] = 'blah';
- $book = new BookFormat;
- $book->name = 'blah';
- $book->save();
- $this->assert_true($book->errors->is_invalid('name'));
- }
-
- public function test_custom_message()
- {
- BookFormat::$validates_format_of[0]['message'] = 'is using a custom message.';
- BookFormat::$validates_format_of[0]['with'] = '/[^0-9]/';
-
- $book = new BookFormat;
- $book->name = null;
- $book->save();
- $this->assert_equals('is using a custom message.', $book->errors->on('name'));
- }
-};
-?>
\ No newline at end of file
+ 1, 'name' => 'testing reg'));
+ $book->save();
+ $this->assert_false($book->errors->is_invalid('name'));
+
+ BookFormat::$validates_format_of[0]['with'] = '/[0-9]/';
+ $book = new BookFormat(array('author_id' => 1, 'name' => 12));
+ $book->save();
+ $this->assert_false($book->errors->is_invalid('name'));
+ }
+
+ public function test_invalid_null()
+ {
+ BookFormat::$validates_format_of[0]['with'] = '/[^0-9]/';
+ $book = new BookFormat;
+ $book->name = null;
+ $book->save();
+ $this->assert_true($book->errors->is_invalid('name'));
+ }
+
+ public function test_invalid_blank()
+ {
+ BookFormat::$validates_format_of[0]['with'] = '/[^0-9]/';
+ $book = new BookFormat;
+ $book->name = '';
+ $book->save();
+ $this->assert_true($book->errors->is_invalid('name'));
+ }
+
+ public function test_valid_blank_andallow_blank()
+ {
+ BookFormat::$validates_format_of[0]['allow_blank'] = true;
+ BookFormat::$validates_format_of[0]['with'] = '/[^0-9]/';
+ $book = new BookFormat(array('author_id' => 1, 'name' => ''));
+ $book->save();
+ $this->assert_false($book->errors->is_invalid('name'));
+ }
+
+ public function test_valid_null_and_allow_null()
+ {
+ BookFormat::$validates_format_of[0]['allow_null'] = true;
+ BookFormat::$validates_format_of[0]['with'] = '/[^0-9]/';
+ $book = new BookFormat();
+ $book->author_id = 1;
+ $book->name = null;
+ $book->save();
+ $this->assert_false($book->errors->is_invalid('name'));
+ }
+
+ /**
+ * @expectedException ActiveRecord\ValidationsArgumentError
+ */
+ public function test_invalid_lack_of_with_key()
+ {
+ $book = new BookFormat;
+ $book->name = null;
+ $book->save();
+ }
+
+ /**
+ * @expectedException ActiveRecord\ValidationsArgumentError
+ */
+ public function test_invalid_with_expression_as_non_string()
+ {
+ BookFormat::$validates_format_of[0]['with'] = array('test');
+ $book = new BookFormat;
+ $book->name = null;
+ $book->save();
+ }
+
+ public function test_invalid_with_expression_as_non_regexp()
+ {
+ BookFormat::$validates_format_of[0]['with'] = 'blah';
+ $book = new BookFormat;
+ $book->name = 'blah';
+ $book->save();
+ $this->assert_true($book->errors->is_invalid('name'));
+ }
+
+ public function test_custom_message()
+ {
+ BookFormat::$validates_format_of[0]['message'] = 'is using a custom message.';
+ BookFormat::$validates_format_of[0]['with'] = '/[^0-9]/';
+
+ $book = new BookFormat;
+ $book->name = null;
+ $book->save();
+ $this->assert_equals('is using a custom message.', $book->errors->on('name'));
+ }
+}
diff --git a/test/ValidatesInclusionAndExclusionOfTest.php b/test/ValidatesInclusionAndExclusionOfTest.php
index ade280165..efa6a28f2 100644
--- a/test/ValidatesInclusionAndExclusionOfTest.php
+++ b/test/ValidatesInclusionAndExclusionOfTest.php
@@ -1,157 +1,156 @@
- array('blah', 'alpha', 'bravo'))
- );
-};
-
-class BookInclusion extends ActiveRecord\Model
-{
- static $table = 'books';
- public static $validates_inclusion_of = array(
- array('name', 'in' => array('blah', 'tanker', 'shark'))
- );
-};
-
-class ValidatesInclusionAndExclusionOfTest extends DatabaseTest
-{
- public function set_up($connection_name=null)
- {
- parent::set_up($connection_name);
- BookInclusion::$validates_inclusion_of[0] = array('name', 'in' => array('blah', 'tanker', 'shark'));
- BookExclusion::$validates_exclusion_of[0] = array('name', 'in' => array('blah', 'alpha', 'bravo'));
- }
-
- public function test_inclusion()
- {
- $book = new BookInclusion;
- $book->name = 'blah';
- $book->save();
- $this->assert_false($book->errors->is_invalid('name'));
- }
-
- public function test_exclusion()
- {
- $book = new BookExclusion;
- $book->name = 'blahh';
- $book->save();
- $this->assert_false($book->errors->is_invalid('name'));
- }
-
- public function test_invalid_inclusion()
- {
- $book = new BookInclusion;
- $book->name = 'thanker';
- $book->save();
- $this->assert_true($book->errors->is_invalid('name'));
- $book->name = 'alpha ';
- $book->save();
- $this->assert_true($book->errors->is_invalid('name'));
- }
-
- public function test_invalid_exclusion()
- {
- $book = new BookExclusion;
- $book->name = 'alpha';
- $book->save();
- $this->assert_true($book->errors->is_invalid('name'));
-
- $book = new BookExclusion;
- $book->name = 'bravo';
- $book->save();
- $this->assert_true($book->errors->is_invalid('name'));
- }
-
- public function test_inclusion_with_numeric()
- {
- BookInclusion::$validates_inclusion_of[0]['in']= array(0, 1, 2);
- $book = new BookInclusion;
- $book->name = 2;
- $book->save();
- $this->assert_false($book->errors->is_invalid('name'));
- }
-
- public function test_inclusion_with_boolean()
- {
- BookInclusion::$validates_inclusion_of[0]['in']= array(true);
- $book = new BookInclusion;
- $book->name = true;
- $book->save();
- $this->assert_false($book->errors->is_invalid('name'));
- }
-
- public function test_inclusion_with_null()
- {
- BookInclusion::$validates_inclusion_of[0]['in']= array(null);
- $book = new BookInclusion;
- $book->name = null;
- $book->save();
- $this->assert_false($book->errors->is_invalid('name'));
- }
-
- public function test_invalid_inclusion_with_numeric()
- {
- BookInclusion::$validates_inclusion_of[0]['in']= array(0, 1, 2);
- $book = new BookInclusion;
- $book->name = 5;
- $book->save();
- $this->assert_true($book->errors->is_invalid('name'));
- }
-
- public function tes_inclusion_within_option()
- {
- BookInclusion::$validates_inclusion_of[0] = array('name', 'within' => array('okay'));
- $book = new BookInclusion;
- $book->name = 'okay';
- $book->save();
- $this->assert_false($book->errors->is_invalid('name'));
- }
-
- public function tes_inclusion_scalar_value()
- {
- BookInclusion::$validates_inclusion_of[0] = array('name', 'within' => 'okay');
- $book = new BookInclusion;
- $book->name = 'okay';
- $book->save();
- $this->assert_false($book->errors->is_invalid('name'));
- }
-
- public function test_valid_null()
- {
- BookInclusion::$validates_inclusion_of[0]['allow_null'] = true;
- $book = new BookInclusion;
- $book->name = null;
- $book->save();
- $this->assert_false($book->errors->is_invalid('name'));
- }
-
- public function test_valid_blank()
- {
- BookInclusion::$validates_inclusion_of[0]['allow_blank'] = true;
- $book = new BookInclusion;
- $book->name = '';
- $book->save();
- $this->assert_false($book->errors->is_invalid('name'));
- }
-
- public function test_custom_message()
- {
- $msg = 'is using a custom message.';
- BookInclusion::$validates_inclusion_of[0]['message'] = $msg;
- BookExclusion::$validates_exclusion_of[0]['message'] = $msg;
-
- $book = new BookInclusion;
- $book->name = 'not included';
- $book->save();
- $this->assert_equals('is using a custom message.', $book->errors->on('name'));
- $book = new BookExclusion;
- $book->name = 'bravo';
- $book->save();
- $this->assert_equals('is using a custom message.', $book->errors->on('name'));
- }
-
-};
-?>
\ No newline at end of file
+ array('blah', 'alpha', 'bravo'))
+ );
+}
+
+class BookInclusion extends ActiveRecord\Model
+{
+ static $table = 'books';
+ public static $validates_inclusion_of = array(
+ array('name', 'in' => array('blah', 'tanker', 'shark'))
+ );
+}
+
+class ValidatesInclusionAndExclusionOfTest extends DatabaseTest
+{
+ public function set_up($connection_name=null)
+ {
+ parent::set_up($connection_name);
+ BookInclusion::$validates_inclusion_of[0] = array('name', 'in' => array('blah', 'tanker', 'shark'));
+ BookExclusion::$validates_exclusion_of[0] = array('name', 'in' => array('blah', 'alpha', 'bravo'));
+ }
+
+ public function test_inclusion()
+ {
+ $book = new BookInclusion;
+ $book->name = 'blah';
+ $book->save();
+ $this->assert_false($book->errors->is_invalid('name'));
+ }
+
+ public function test_exclusion()
+ {
+ $book = new BookExclusion;
+ $book->name = 'blahh';
+ $book->save();
+ $this->assert_false($book->errors->is_invalid('name'));
+ }
+
+ public function test_invalid_inclusion()
+ {
+ $book = new BookInclusion;
+ $book->name = 'thanker';
+ $book->save();
+ $this->assert_true($book->errors->is_invalid('name'));
+ $book->name = 'alpha ';
+ $book->save();
+ $this->assert_true($book->errors->is_invalid('name'));
+ }
+
+ public function test_invalid_exclusion()
+ {
+ $book = new BookExclusion;
+ $book->name = 'alpha';
+ $book->save();
+ $this->assert_true($book->errors->is_invalid('name'));
+
+ $book = new BookExclusion;
+ $book->name = 'bravo';
+ $book->save();
+ $this->assert_true($book->errors->is_invalid('name'));
+ }
+
+ public function test_inclusion_with_numeric()
+ {
+ BookInclusion::$validates_inclusion_of[0]['in']= array(0, 1, 2);
+ $book = new BookInclusion;
+ $book->name = 2;
+ $book->save();
+ $this->assert_false($book->errors->is_invalid('name'));
+ }
+
+ public function test_inclusion_with_boolean()
+ {
+ BookInclusion::$validates_inclusion_of[0]['in']= array(true);
+ $book = new BookInclusion;
+ $book->name = true;
+ $book->save();
+ $this->assert_false($book->errors->is_invalid('name'));
+ }
+
+ public function test_inclusion_with_null()
+ {
+ BookInclusion::$validates_inclusion_of[0]['in']= array(null);
+ $book = new BookInclusion;
+ $book->name = null;
+ $book->save();
+ $this->assert_false($book->errors->is_invalid('name'));
+ }
+
+ public function test_invalid_inclusion_with_numeric()
+ {
+ BookInclusion::$validates_inclusion_of[0]['in']= array(0, 1, 2);
+ $book = new BookInclusion;
+ $book->name = 5;
+ $book->save();
+ $this->assert_true($book->errors->is_invalid('name'));
+ }
+
+ public function tes_inclusion_within_option()
+ {
+ BookInclusion::$validates_inclusion_of[0] = array('name', 'within' => array('okay'));
+ $book = new BookInclusion;
+ $book->name = 'okay';
+ $book->save();
+ $this->assert_false($book->errors->is_invalid('name'));
+ }
+
+ public function tes_inclusion_scalar_value()
+ {
+ BookInclusion::$validates_inclusion_of[0] = array('name', 'within' => 'okay');
+ $book = new BookInclusion;
+ $book->name = 'okay';
+ $book->save();
+ $this->assert_false($book->errors->is_invalid('name'));
+ }
+
+ public function test_valid_null()
+ {
+ BookInclusion::$validates_inclusion_of[0]['allow_null'] = true;
+ $book = new BookInclusion;
+ $book->name = null;
+ $book->save();
+ $this->assert_false($book->errors->is_invalid('name'));
+ }
+
+ public function test_valid_blank()
+ {
+ BookInclusion::$validates_inclusion_of[0]['allow_blank'] = true;
+ $book = new BookInclusion;
+ $book->name = '';
+ $book->save();
+ $this->assert_false($book->errors->is_invalid('name'));
+ }
+
+ public function test_custom_message()
+ {
+ $msg = 'is using a custom message.';
+ BookInclusion::$validates_inclusion_of[0]['message'] = $msg;
+ BookExclusion::$validates_exclusion_of[0]['message'] = $msg;
+
+ $book = new BookInclusion;
+ $book->name = 'not included';
+ $book->save();
+ $this->assert_equals('is using a custom message.', $book->errors->on('name'));
+ $book = new BookExclusion;
+ $book->name = 'bravo';
+ $book->save();
+ $this->assert_equals('is using a custom message.', $book->errors->on('name'));
+ }
+
+}
diff --git a/test/ValidatesLengthOfTest.php b/test/ValidatesLengthOfTest.php
index bd93a1d80..99ebe786c 100644
--- a/test/ValidatesLengthOfTest.php
+++ b/test/ValidatesLengthOfTest.php
@@ -1,360 +1,334 @@
- false, 'allow_null' => false);
- }
-
- public function test_within()
- {
- BookLength::$validates_length_of[0]['within'] = array(1, 5);
- $book = new BookLength;
- $book->name = '12345';
- $book->save();
- $this->assert_false($book->errors->is_invalid('name'));
- }
-
- public function test_within_error_message()
- {
- BookLength::$validates_length_of[0]['within'] = array(2,5);
- $book = new BookLength();
- $book->name = '1';
- $book->is_valid();
- $this->assert_equals(array('Name is too short (minimum is 2 characters)'),$book->errors->full_messages());
-
- $book->name = '123456';
- $book->is_valid();
- $this->assert_equals(array('Name is too long (maximum is 5 characters)'),$book->errors->full_messages());
- }
-
- public function test_within_custom_error_message()
- {
- BookLength::$validates_length_of[0]['within'] = array(2,5);
- BookLength::$validates_length_of[0]['too_short'] = 'is too short';
- BookLength::$validates_length_of[0]['message'] = 'is not between 2 and 5 characters';
- $book = new BookLength();
- $book->name = '1';
- $book->is_valid();
- $this->assert_equals(array('Name is not between 2 and 5 characters'),$book->errors->full_messages());
-
- $book->name = '123456';
- $book->is_valid();
- $this->assert_equals(array('Name is not between 2 and 5 characters'),$book->errors->full_messages());
- }
-
- public function test_valid_in()
- {
- BookLength::$validates_length_of[0]['in'] = array(1, 5);
- $book = new BookLength;
- $book->name = '12345';
- $book->save();
- $this->assert_false($book->errors->is_invalid('name'));
- }
-
- public function test_aliased_size_of()
- {
- BookSize::$validates_size_of = BookLength::$validates_length_of;
- BookSize::$validates_size_of[0]['within'] = array(1, 5);
- $book = new BookSize;
- $book->name = '12345';
- $book->save();
- $this->assert_false($book->errors->is_invalid('name'));
- }
-
- public function test_invalid_within_and_in()
- {
- BookLength::$validates_length_of[0]['within'] = array(1, 3);
- $book = new BookLength;
- $book->name = 'four';
- $book->save();
- $this->assert_true($book->errors->is_invalid('name'));
-
- $this->set_up();
- BookLength::$validates_length_of[0]['in'] = array(1, 3);
- $book = new BookLength;
- $book->name = 'four';
- $book->save();
- $this->assert_true($book->errors->is_invalid('name'));
- }
-
- public function test_valid_null()
- {
- BookLength::$validates_length_of[0]['within'] = array(1, 3);
- BookLength::$validates_length_of[0]['allow_null'] = true;
-
- $book = new BookLength;
- $book->name = null;
- $book->save();
- $this->assert_false($book->errors->is_invalid('name'));
- }
-
- public function test_valid_blank()
- {
- BookLength::$validates_length_of[0]['within'] = array(1, 3);
- BookLength::$validates_length_of[0]['allow_blank'] = true;
-
- $book = new BookLength;
- $book->name = '';
- $book->save();
- $this->assert_false($book->errors->is_invalid('name'));
- }
-
- public function test_invalid_blank()
- {
- BookLength::$validates_length_of[0]['within'] = array(1, 3);
-
- $book = new BookLength;
- $book->name = '';
- $book->save();
- $this->assert_true($book->errors->is_invalid('name'));
- $this->assert_equals('is too short (minimum is 1 characters)', $book->errors->on('name'));
- }
-
- public function test_invalid_null_within()
- {
- BookLength::$validates_length_of[0]['within'] = array(1, 3);
-
- $book = new BookLength;
- $book->name = null;
- $book->save();
- $this->assert_true($book->errors->is_invalid('name'));
- $this->assert_equals('is too short (minimum is 1 characters)', $book->errors->on('name'));
- }
-
- public function test_invalid_null_minimum()
- {
- BookLength::$validates_length_of[0]['minimum'] = 1;
-
- $book = new BookLength;
- $book->name = null;
- $book->save();
- $this->assert_true($book->errors->is_invalid('name'));
- $this->assert_equals('is too short (minimum is 1 characters)', $book->errors->on('name'));
-
- }
-
- public function test_valid_null_maximum()
- {
- BookLength::$validates_length_of[0]['maximum'] = 1;
-
- $book = new BookLength;
- $book->name = null;
- $book->save();
- $this->assert_false($book->errors->is_invalid('name'));
- }
-
- public function test_float_as_impossible_range_option()
- {
- BookLength::$validates_length_of[0]['within'] = array(1, 3.6);
- $book = new BookLength;
- $book->name = '123';
- try {
- $book->save();
- } catch (ActiveRecord\ValidationsArgumentError $e) {
- $this->assert_equals('maximum value cannot use a float for length.', $e->getMessage());
- }
-
- $this->set_up();
- BookLength::$validates_length_of[0]['is'] = 1.8;
- $book = new BookLength;
- $book->name = '123';
- try {
- $book->save();
- } catch (ActiveRecord\ValidationsArgumentError $e) {
- $this->assert_equals('is value cannot use a float for length.', $e->getMessage());
- return;
- }
-
- $this->fail('An expected exception has not be raised.');
- }
-
- public function test_signed_integer_as_impossible_within_option()
- {
- BookLength::$validates_length_of[0]['within'] = array(-1, 3);
-
- $book = new BookLength;
- $book->name = '123';
- try {
- $book->save();
- } catch (ActiveRecord\ValidationsArgumentError $e) {
- $this->assert_equals('minimum value cannot use a signed integer.', $e->getMessage());
- return;
- }
-
- $this->fail('An expected exception has not be raised.');
- }
-
- public function test_not_array_as_impossible_range_option()
- {
- BookLength::$validates_length_of[0]['within'] = 'string';
- $book = new BookLength;
- $book->name = '123';
- try {
- $book->save();
- } catch (ActiveRecord\ValidationsArgumentError $e) {
- $this->assert_equals('within must be an array composing a range of numbers with key [0] being less than key [1]', $e->getMessage());
- }
-
- $this->set_up();
- BookLength::$validates_length_of[0]['in'] = 'string';
- $book = new BookLength;
- $book->name = '123';
- try {
- $book->save();
- } catch (ActiveRecord\ValidationsArgumentError $e) {
- $this->assert_equals('in must be an array composing a range of numbers with key [0] being less than key [1]', $e->getMessage());
- return;
- }
-
- $this->fail('An expected exception has not be raised.');
- }
-
- public function test_signed_integer_as_impossible_is_option()
- {
- BookLength::$validates_length_of[0]['is'] = -8;
-
- $book = new BookLength;
- $book->name = '123';
- try {
- $book->save();
- } catch (ActiveRecord\ValidationsArgumentError $e) {
- $this->assert_equals('is value cannot use a signed integer.', $e->getMessage());
- return;
- }
-
- $this->fail('An expected exception has not be raised.');
- }
-
- public function test_lack_of_option()
- {
- try {
- $book = new BookLength;
- $book->name = null;
- $book->save();
- } catch (ActiveRecord\ValidationsArgumentError $e) {
- $this->assert_equals('Range unspecified. Specify the [within], [maximum], or [is] option.', $e->getMessage());
- return;
- }
-
- $this->fail('An expected exception has not be raised.');
- }
-
- public function test_too_many_options()
- {
- BookLength::$validates_length_of[0]['within'] = array(1, 3);
- BookLength::$validates_length_of[0]['in'] = array(1, 3);
-
- try {
- $book = new BookLength;
- $book->name = null;
- $book->save();
- } catch (ActiveRecord\ValidationsArgumentError $e) {
- $this->assert_equals('Too many range options specified. Choose only one.', $e->getMessage());
- return;
- }
-
- $this->fail('An expected exception has not be raised.');
- }
-
- public function test_too_many_options_with_different_option_types()
- {
- BookLength::$validates_length_of[0]['within'] = array(1, 3);
- BookLength::$validates_length_of[0]['is'] = 3;
-
- try {
- $book = new BookLength;
- $book->name = null;
- $book->save();
- } catch (ActiveRecord\ValidationsArgumentError $e) {
- $this->assert_equals('Too many range options specified. Choose only one.', $e->getMessage());
- return;
- }
-
- $this->fail('An expected exception has not be raised.');
- }
-
- /**
- * @expectedException ActiveRecord\ValidationsArgumentError
- */
- public function test_with_option_as_non_numeric()
- {
- BookLength::$validates_length_of[0]['with'] = array('test');
-
- $book = new BookLength;
- $book->name = null;
- $book->save();
- }
-
- /**
- * @expectedException ActiveRecord\ValidationsArgumentError
- */
- public function test_with_option_as_non_numeric_non_array()
- {
- BookLength::$validates_length_of[0]['with'] = 'test';
-
- $book = new BookLength;
- $book->name = null;
- $book->save();
- }
-
- public function test_validates_length_of_maximum()
- {
- BookLength::$validates_length_of[0] = array('name', 'maximum' => 10);
- $book = new BookLength(array('name' => '12345678901'));
- $book->is_valid();
- $this->assert_equals(array("Name is too long (maximum is 10 characters)"),$book->errors->full_messages());
- }
-
- public function test_validates_length_of_minimum()
- {
- BookLength::$validates_length_of[0] = array('name', 'minimum' => 2);
- $book = new BookLength(array('name' => '1'));
- $book->is_valid();
- $this->assert_equals(array("Name is too short (minimum is 2 characters)"),$book->errors->full_messages());
- }
-
- public function test_validates_length_of_min_max_custom_message()
- {
- BookLength::$validates_length_of[0] = array('name', 'maximum' => 10, 'message' => 'is far too long');
- $book = new BookLength(array('name' => '12345678901'));
- $book->is_valid();
- $this->assert_equals(array("Name is far too long"),$book->errors->full_messages());
-
- BookLength::$validates_length_of[0] = array('name', 'minimum' => 10, 'message' => 'is far too short');
- $book = new BookLength(array('name' => '123456789'));
- $book->is_valid();
- $this->assert_equals(array("Name is far too short"),$book->errors->full_messages());
- }
-
- public function test_validates_length_of_min_max_custom_message_overridden()
- {
- BookLength::$validates_length_of[0] = array('name', 'minimum' => 10, 'too_short' => 'is too short', 'message' => 'is custom message');
- $book = new BookLength(array('name' => '123456789'));
- $book->is_valid();
- $this->assert_equals(array("Name is custom message"),$book->errors->full_messages());
- }
-
- public function test_validates_length_of_is()
- {
- BookLength::$validates_length_of[0] = array('name', 'is' => 2);
- $book = new BookLength(array('name' => '123'));
- $book->is_valid();
- $this->assert_equals(array("Name is the wrong length (should be 2 characters)"),$book->errors->full_messages());
- }
-};
-?>
\ No newline at end of file
+ false, 'allow_null' => false);
+ }
+
+ public function test_within()
+ {
+ BookLength::$validates_length_of[0]['within'] = array(1, 5);
+ $book = new BookLength;
+ $book->name = '12345';
+ $book->save();
+ $this->assert_false($book->errors->is_invalid('name'));
+ }
+
+ public function test_within_error_message()
+ {
+ BookLength::$validates_length_of[0]['within'] = array(2,5);
+ $book = new BookLength();
+ $book->name = '1';
+ $book->is_valid();
+ $this->assert_equals(array('Name is too short (minimum is 2 characters)'),$book->errors->full_messages());
+
+ $book->name = '123456';
+ $book->is_valid();
+ $this->assert_equals(array('Name is too long (maximum is 5 characters)'),$book->errors->full_messages());
+ }
+
+ public function test_within_custom_error_message()
+ {
+ BookLength::$validates_length_of[0]['within'] = array(2,5);
+ BookLength::$validates_length_of[0]['too_short'] = 'is too short';
+ BookLength::$validates_length_of[0]['message'] = 'is not between 2 and 5 characters';
+ $book = new BookLength();
+ $book->name = '1';
+ $book->is_valid();
+ $this->assert_equals(array('Name is not between 2 and 5 characters'),$book->errors->full_messages());
+
+ $book->name = '123456';
+ $book->is_valid();
+ $this->assert_equals(array('Name is not between 2 and 5 characters'),$book->errors->full_messages());
+ }
+
+ public function test_valid_in()
+ {
+ BookLength::$validates_length_of[0]['in'] = array(1, 5);
+ $book = new BookLength;
+ $book->name = '12345';
+ $book->save();
+ $this->assert_false($book->errors->is_invalid('name'));
+ }
+
+ public function test_aliased_size_of()
+ {
+ BookSize::$validates_size_of = BookLength::$validates_length_of;
+ BookSize::$validates_size_of[0]['within'] = array(1, 5);
+ $book = new BookSize;
+ $book->name = '12345';
+ $book->save();
+ $this->assert_false($book->errors->is_invalid('name'));
+ }
+
+ public function test_invalid_within_and_in()
+ {
+ BookLength::$validates_length_of[0]['within'] = array(1, 3);
+ $book = new BookLength;
+ $book->name = 'four';
+ $book->save();
+ $this->assert_true($book->errors->is_invalid('name'));
+
+ $this->set_up();
+ BookLength::$validates_length_of[0]['in'] = array(1, 3);
+ $book = new BookLength;
+ $book->name = 'four';
+ $book->save();
+ $this->assert_true($book->errors->is_invalid('name'));
+ }
+
+ public function test_valid_null()
+ {
+ BookLength::$validates_length_of[0]['within'] = array(1, 3);
+ BookLength::$validates_length_of[0]['allow_null'] = true;
+
+ $book = new BookLength;
+ $book->name = null;
+ $book->save();
+ $this->assert_false($book->errors->is_invalid('name'));
+ }
+
+ public function test_valid_blank()
+ {
+ BookLength::$validates_length_of[0]['within'] = array(1, 3);
+ BookLength::$validates_length_of[0]['allow_blank'] = true;
+
+ $book = new BookLength;
+ $book->name = '';
+ $book->save();
+ $this->assert_false($book->errors->is_invalid('name'));
+ }
+
+ public function test_invalid_blank()
+ {
+ BookLength::$validates_length_of[0]['within'] = array(1, 3);
+
+ $book = new BookLength;
+ $book->name = '';
+ $book->save();
+ $this->assert_true($book->errors->is_invalid('name'));
+ $this->assert_equals('is too short (minimum is 1 characters)', $book->errors->on('name'));
+ }
+
+ public function test_invalid_null_within()
+ {
+ BookLength::$validates_length_of[0]['within'] = array(1, 3);
+
+ $book = new BookLength;
+ $book->name = null;
+ $book->save();
+ $this->assert_true($book->errors->is_invalid('name'));
+ $this->assert_equals('is too short (minimum is 1 characters)', $book->errors->on('name'));
+ }
+
+ public function test_invalid_null_minimum()
+ {
+ BookLength::$validates_length_of[0]['minimum'] = 1;
+
+ $book = new BookLength;
+ $book->name = null;
+ $book->save();
+ $this->assert_true($book->errors->is_invalid('name'));
+ $this->assert_equals('is too short (minimum is 1 characters)', $book->errors->on('name'));
+
+ }
+
+ public function test_valid_null_maximum()
+ {
+ BookLength::$validates_length_of[0]['maximum'] = 1;
+
+ $book = new BookLength;
+ $book->name = null;
+ $book->save();
+ $this->assert_false($book->errors->is_invalid('name'));
+ }
+
+ public function test_float_as_impossible_range_option()
+ {
+ BookLength::$validates_length_of[0]['within'] = array(1, 3.6);
+ $book = new BookLength;
+ $book->name = '123';
+ try {
+ $book->save();
+ } catch (ActiveRecord\ValidationsArgumentError $e) {
+ $this->assert_equals('maximum value cannot use a float for length.', $e->getMessage());
+ }
+
+ $this->set_up();
+ BookLength::$validates_length_of[0]['is'] = 1.8;
+ $book = new BookLength;
+ $book->name = '123';
+ try {
+ $book->save();
+ } catch (ActiveRecord\ValidationsArgumentError $e) {
+ $this->assert_equals('is value cannot use a float for length.', $e->getMessage());
+ return;
+ }
+
+ $this->fail('An expected exception has not be raised.');
+ }
+
+ public function test_signed_integer_as_impossible_within_option()
+ {
+ BookLength::$validates_length_of[0]['within'] = array(-1, 3);
+
+ $book = new BookLength;
+ $book->name = '123';
+ try {
+ $book->save();
+ } catch (ActiveRecord\ValidationsArgumentError $e) {
+ $this->assert_equals('minimum value cannot use a signed integer.', $e->getMessage());
+ return;
+ }
+
+ $this->fail('An expected exception has not be raised.');
+ }
+
+ public function test_signed_integer_as_impossible_is_option()
+ {
+ BookLength::$validates_length_of[0]['is'] = -8;
+
+ $book = new BookLength;
+ $book->name = '123';
+ try {
+ $book->save();
+ } catch (ActiveRecord\ValidationsArgumentError $e) {
+ $this->assert_equals('is value cannot use a signed integer.', $e->getMessage());
+ return;
+ }
+
+ $this->fail('An expected exception has not be raised.');
+ }
+
+ public function test_lack_of_option()
+ {
+ try {
+ $book = new BookLength;
+ $book->name = null;
+ $book->save();
+ } catch (ActiveRecord\ValidationsArgumentError $e) {
+ $this->assert_equals('Range unspecified. Specify the [within], [maximum], or [is] option.', $e->getMessage());
+ return;
+ }
+
+ $this->fail('An expected exception has not be raised.');
+ }
+
+ public function test_too_many_options()
+ {
+ BookLength::$validates_length_of[0]['within'] = array(1, 3);
+ BookLength::$validates_length_of[0]['in'] = array(1, 3);
+
+ try {
+ $book = new BookLength;
+ $book->name = null;
+ $book->save();
+ } catch (ActiveRecord\ValidationsArgumentError $e) {
+ $this->assert_equals('Too many range options specified. Choose only one.', $e->getMessage());
+ return;
+ }
+
+ $this->fail('An expected exception has not be raised.');
+ }
+
+ public function test_too_many_options_with_different_option_types()
+ {
+ BookLength::$validates_length_of[0]['within'] = array(1, 3);
+ BookLength::$validates_length_of[0]['is'] = 3;
+
+ try {
+ $book = new BookLength;
+ $book->name = null;
+ $book->save();
+ } catch (ActiveRecord\ValidationsArgumentError $e) {
+ $this->assert_equals('Too many range options specified. Choose only one.', $e->getMessage());
+ return;
+ }
+
+ $this->fail('An expected exception has not be raised.');
+ }
+
+ /**
+ * @expectedException ActiveRecord\ValidationsArgumentError
+ */
+ public function test_with_option_as_non_numeric()
+ {
+ BookLength::$validates_length_of[0]['with'] = array('test');
+
+ $book = new BookLength;
+ $book->name = null;
+ $book->save();
+ }
+
+ /**
+ * @expectedException ActiveRecord\ValidationsArgumentError
+ */
+ public function test_with_option_as_non_numeric_non_array()
+ {
+ BookLength::$validates_length_of[0]['with'] = 'test';
+
+ $book = new BookLength;
+ $book->name = null;
+ $book->save();
+ }
+
+ public function test_validates_length_of_maximum()
+ {
+ BookLength::$validates_length_of[0] = array('name', 'maximum' => 10);
+ $book = new BookLength(array('name' => '12345678901'));
+ $book->is_valid();
+ $this->assert_equals(array("Name is too long (maximum is 10 characters)"),$book->errors->full_messages());
+ }
+
+ public function test_validates_length_of_minimum()
+ {
+ BookLength::$validates_length_of[0] = array('name', 'minimum' => 2);
+ $book = new BookLength(array('name' => '1'));
+ $book->is_valid();
+ $this->assert_equals(array("Name is too short (minimum is 2 characters)"),$book->errors->full_messages());
+ }
+
+ public function test_validates_length_of_min_max_custom_message()
+ {
+ BookLength::$validates_length_of[0] = array('name', 'maximum' => 10, 'message' => 'is far too long');
+ $book = new BookLength(array('name' => '12345678901'));
+ $book->is_valid();
+ $this->assert_equals(array("Name is far too long"),$book->errors->full_messages());
+
+ BookLength::$validates_length_of[0] = array('name', 'minimum' => 10, 'message' => 'is far too short');
+ $book = new BookLength(array('name' => '123456789'));
+ $book->is_valid();
+ $this->assert_equals(array("Name is far too short"),$book->errors->full_messages());
+ }
+
+ public function test_validates_length_of_min_max_custom_message_overridden()
+ {
+ BookLength::$validates_length_of[0] = array('name', 'minimum' => 10, 'too_short' => 'is too short', 'message' => 'is custom message');
+ $book = new BookLength(array('name' => '123456789'));
+ $book->is_valid();
+ $this->assert_equals(array("Name is custom message"),$book->errors->full_messages());
+ }
+
+ public function test_validates_length_of_is()
+ {
+ BookLength::$validates_length_of[0] = array('name', 'is' => 2);
+ $book = new BookLength(array('name' => '123'));
+ $book->is_valid();
+ $this->assert_equals(array("Name is the wrong length (should be 2 characters)"),$book->errors->full_messages());
+ }
+}
diff --git a/test/ValidatesNumericalityOfTest.php b/test/ValidatesNumericalityOfTest.php
index 08eb7f2ec..ac9d6e2e3 100644
--- a/test/ValidatesNumericalityOfTest.php
+++ b/test/ValidatesNumericalityOfTest.php
@@ -158,8 +158,7 @@ public function test_custom_message()
$book->is_valid();
$this->assert_equals(array('Numeric test Hello'),$book->errors->full_messages());
}
-};
+}
array_merge(ValidatesNumericalityOfTest::$INTEGERS, ValidatesNumericalityOfTest::$INTEGER_STRINGS);
array_merge(ValidatesNumericalityOfTest::$FLOATS, ValidatesNumericalityOfTest::$FLOAT_STRINGS);
-?>
diff --git a/test/ValidatesOnSaveCreateUpdateTest.php b/test/ValidatesOnSaveCreateUpdateTest.php
new file mode 100644
index 000000000..70f7a9c25
--- /dev/null
+++ b/test/ValidatesOnSaveCreateUpdateTest.php
@@ -0,0 +1,73 @@
+ 'create');
+ $book = new BookOn();
+ $this->assert_false($book->is_valid());
+
+ $book->secondary_author_id = 1;
+ $this->assert_false( $book->save() );
+
+ $book->name = 'Baz';
+ $this->assert_true( $book->save() );
+
+ $book->name = null;
+ $this->assert_true( $book->save() );
+ }
+
+ public function test_validations_only_run_on_update()
+ {
+ BookOn::$validates_presence_of[0] = array('name', 'on' => 'update');
+ $book = new BookOn();
+ $this->assert_true($book->save());
+
+ $book->name = null;
+ $this->assert_false( $book->save() );
+
+ $book->name = 'Baz';
+ $this->assert_true( $book->save() );
+ }
+
+ public function test_validations_only_run_on_save()
+ {
+ BookOn::$validates_presence_of[0] = array('name', 'on' => 'save');
+ $book = new BookOn();
+ $this->assert_false($book->is_valid());
+
+ $book->secondary_author_id = 1;
+ $this->assert_false( $book->save() );
+
+ $book->name = 'Baz';
+ $this->assert_true( $book->save() );
+
+ $book->name = null;
+ $this->assert_false( $book->save() );
+ }
+
+ public function test_validations_run_always_without_on()
+ {
+ BookOn::$validates_presence_of[0] = array('name');
+ $book = new BookOn();
+ $this->assert_false($book->is_valid());
+
+ $book->secondary_author_id = 1;
+ $this->assert_false( $book->save() );
+
+ $book->name = 'Baz';
+ $this->assert_true( $book->save() );
+
+ $book->name = null;
+ $this->assert_false( $book->save() );
+ }
+
+}
\ No newline at end of file
diff --git a/test/ValidatesPresenceOfTest.php b/test/ValidatesPresenceOfTest.php
index 745c55939..16c2354de 100644
--- a/test/ValidatesPresenceOfTest.php
+++ b/test/ValidatesPresenceOfTest.php
@@ -1,74 +1,73 @@
- 'blah'));
- $this->assert_false($book->is_invalid());
- }
-
- public function test_presence_on_date_field_is_valid()
- {
- $author = new AuthorPresence(array('some_date' => '2010-01-01'));
- $this->assert_true($author->is_valid());
- }
-
- public function test_presence_on_date_field_is_not_valid()
- {
- $author = new AuthorPresence();
- $this->assert_false($author->is_valid());
- }
-
- public function test_invalid_null()
- {
- $book = new BookPresence(array('name' => null));
- $this->assert_true($book->is_invalid());
- }
-
- public function test_invalid_blank()
- {
- $book = new BookPresence(array('name' => ''));
- $this->assert_true($book->is_invalid());
- }
-
- public function test_valid_white_space()
- {
- $book = new BookPresence(array('name' => ' '));
- $this->assert_false($book->is_invalid());
- }
-
- public function test_custom_message()
- {
- BookPresence::$validates_presence_of[0]['message'] = 'is using a custom message.';
-
- $book = new BookPresence(array('name' => null));
- $book->is_valid();
- $this->assert_equals('is using a custom message.', $book->errors->on('name'));
- }
-
- public function test_valid_zero()
- {
- $book = new BookPresence(array('name' => 0));
- $this->assert_true($book->is_valid());
- }
-};
-?>
\ No newline at end of file
+ 'blah'));
+ $this->assert_false($book->is_invalid());
+ }
+
+ public function test_presence_on_date_field_is_valid()
+ {
+ $author = new AuthorPresence(array('some_date' => '2010-01-01'));
+ $this->assert_true($author->is_valid());
+ }
+
+ public function test_presence_on_date_field_is_not_valid()
+ {
+ $author = new AuthorPresence();
+ $this->assert_false($author->is_valid());
+ }
+
+ public function test_invalid_null()
+ {
+ $book = new BookPresence(array('name' => null));
+ $this->assert_true($book->is_invalid());
+ }
+
+ public function test_invalid_blank()
+ {
+ $book = new BookPresence(array('name' => ''));
+ $this->assert_true($book->is_invalid());
+ }
+
+ public function test_valid_white_space()
+ {
+ $book = new BookPresence(array('name' => ' '));
+ $this->assert_false($book->is_invalid());
+ }
+
+ public function test_custom_message()
+ {
+ BookPresence::$validates_presence_of[0]['message'] = 'is using a custom message.';
+
+ $book = new BookPresence(array('name' => null));
+ $book->is_valid();
+ $this->assert_equals('is using a custom message.', $book->errors->on('name'));
+ }
+
+ public function test_valid_zero()
+ {
+ $book = new BookPresence(array('name' => 0));
+ $this->assert_true($book->is_valid());
+ }
+}
diff --git a/test/ValidationsTest.php b/test/ValidationsTest.php
index 3e0eef257..d34f9e07c 100644
--- a/test/ValidationsTest.php
+++ b/test/ValidationsTest.php
@@ -18,6 +18,33 @@ public function validate()
}
}
+class UserValidations extends AR\Model
+{
+ static $table_name = 'users';
+
+ public $password_confirm;
+
+ // Only for test purpose. This will double encrypt pass from the DB!
+ public function set_password($pass)
+ {
+ $this->assign_attribute('password', static::encrypt($pass));
+ }
+
+ public function validate()
+ {
+ // Another BAD idea
+ $this->password_confirm = static::encrypt($this->password_confirm);
+ if($this->password_confirm !== $this->password)
+ $this->errors->add('password', 'Password Mismatch');
+ }
+
+ public static function encrypt($data)
+ {
+ return md5($data);
+ }
+
+}
+
class ValuestoreValidations extends ActiveRecord\Model
{
static $table_name = 'valuestore';
@@ -68,6 +95,78 @@ public function test_is_invalid_is_true()
$this->assert_true($book->is_invalid());
}
+ public function test_is_valid_does_not_revalidate()
+ {
+ $attrs = array(
+ 'password' => 'secret',
+ 'password_confirm' => 'secret'
+ );
+
+ $user = new UserValidations($attrs);
+ /**
+ * The `is_valid()` method will validate the User. In this test it will
+ * be valid.
+ * If `is_valid()` had revalidated it again, `password_confirm` would be
+ * rehashed, becoming different from `password` and then the result
+ * would be different from precedent (and also a bug).
+ */
+ $this->assert_true($user->is_valid());
+ $this->assert_equals(!$user->is_valid(), $user->is_invalid());
+ }
+
+ public function test_is_valid_will_revalidate_if_attribute_changes()
+ {
+ $attrs = array(
+ 'password' => 'bad',
+ 'password_confirm' => 'secret'
+ );
+
+ $user = new UserValidations($attrs);
+ $this->assert_false($user->is_valid());
+
+ $user->password = 'secret';
+ // because custom validation is coded bad (on purpose), we have to
+ // reset password_confirm
+ $user->password_confirm = 'secret';
+ $this->assert_true($user->is_valid());
+ }
+
+ public function test_is_invalid_will_revalidate_if_attribute_changes()
+ {
+ $attrs = array(
+ 'password' => 'bad',
+ 'password_confirm' => 'secret'
+ );
+
+ $user = new UserValidations($attrs);
+ $this->assert_true($user->is_invalid());
+
+ $user->password = 'secret';
+ // because custom validation is coded bad (on purpose), we have to
+ // reset password_confirm
+ $user->password_confirm = 'secret';
+ $this->assert_false($user->is_invalid());
+ }
+
+ public function test_is_valid_must_be_forced_if_a_virtual_attribute_changes()
+ {
+ $attrs = array(
+ 'password' => 'secret',
+ 'password_confirm' => 'bad'
+ );
+
+ $user = new UserValidations($attrs);
+ $this->assert_false($user->is_valid());
+
+ $user->password_confirm = 'secret';
+ // Actually we check only attribute set by `__set` magic method.
+ $this->assert_false($user->is_valid());
+
+ // Passing `true` will force the validation of `user`, giving the
+ // right result.
+ $this->assert_true($user->is_valid(true));
+ }
+
public function test_is_iterable()
{
$book = new BookValidations();
@@ -178,5 +277,5 @@ public function test_gh131_custom_validation()
$this->assert_true($book->errors->is_invalid('name'));
$this->assert_equals(BookValidations::$custom_validator_error_msg, $book->errors->on('name'));
}
-};
-?>
+}
+
diff --git a/test/backup-models/Backup.php b/test/backup-models/Backup.php
new file mode 100644
index 000000000..e2a4bab40
--- /dev/null
+++ b/test/backup-models/Backup.php
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/test/backup-models/NamespaceTest/Backup.php b/test/backup-models/NamespaceTest/Backup.php
new file mode 100644
index 000000000..48f77a37e
--- /dev/null
+++ b/test/backup-models/NamespaceTest/Backup.php
@@ -0,0 +1,7 @@
+
\ No newline at end of file
diff --git a/test/fixtures/users.csv b/test/fixtures/users.csv
index 3ff3deb87..5976d917a 100644
--- a/test/fixtures/users.csv
+++ b/test/fixtures/users.csv
@@ -1,2 +1,2 @@
-id
-1
+id,password
+1,hello
diff --git a/test/helpers/AdapterTest.php b/test/helpers/AdapterTest.php
index 97685f6bc..b4a156c26 100644
--- a/test/helpers/AdapterTest.php
+++ b/test/helpers/AdapterTest.php
@@ -8,7 +8,7 @@ class AdapterTest extends DatabaseTest
public function set_up($connection_name=null)
{
if (($connection_name && !in_array($connection_name, PDO::getAvailableDrivers())) ||
- ActiveRecord\Config::instance()->get_connection($connection_name) == 'skip')
+ ActiveRecord\Config::instance()->get_connection_info($connection_name) == 'skip')
$this->mark_test_skipped($connection_name . ' drivers are not present');
parent::set_up($connection_name);
@@ -25,7 +25,7 @@ public function test_i_has_a_default_port_unless_im_sqlite()
public function test_should_set_adapter_variables()
{
- $this->assert_not_null($this->conn->protocol);
+ $this->assert_not_null($this->conn->adapter);
}
public function test_null_connection_string_uses_default_connection()
@@ -38,7 +38,7 @@ public function test_null_connection_string_uses_default_connection()
/**
* @expectedException ActiveRecord\DatabaseException
*/
- public function test_invalid_connection_protocol()
+ public function test_invalid_connection_adapter()
{
ActiveRecord\Connection::instance('terribledb://user:pass@host/db');
}
@@ -51,7 +51,7 @@ public function test_no_host_connection()
if (!$GLOBALS['slow_tests'])
throw new ActiveRecord\DatabaseException("");
- ActiveRecord\Connection::instance("{$this->conn->protocol}://user:pass");
+ ActiveRecord\Connection::instance("{$this->conn->adapter}://user:pass");
}
/**
@@ -62,7 +62,7 @@ public function test_connection_failed_invalid_host()
if (!$GLOBALS['slow_tests'])
throw new ActiveRecord\DatabaseException("");
- ActiveRecord\Connection::instance("{$this->conn->protocol}://user:pass/1.1.1.1/db");
+ ActiveRecord\Connection::instance("{$this->conn->adapter}://user:pass/1.1.1.1/db");
}
/**
@@ -70,7 +70,7 @@ public function test_connection_failed_invalid_host()
*/
public function test_connection_failed()
{
- ActiveRecord\Connection::instance("{$this->conn->protocol}://baduser:badpass@127.0.0.1/db");
+ ActiveRecord\Connection::instance("{$this->conn->adapter}://baduser:badpass@127.0.0.1/db");
}
/**
@@ -78,25 +78,32 @@ public function test_connection_failed()
*/
public function test_connect_failed()
{
- ActiveRecord\Connection::instance("{$this->conn->protocol}://zzz:zzz@127.0.0.1/test");
+ ActiveRecord\Connection::instance("{$this->conn->adapter}://zzz:zzz@127.0.0.1/test");
}
public function test_connect_with_port()
{
$config = ActiveRecord\Config::instance();
- $name = $config->get_default_connection();
- $url = parse_url($config->get_connection($name));
+ $info = $config->get_default_connection_info();
+
$conn = $this->conn;
$port = $conn::$DEFAULT_PORT;
- $connection_string = "{$url['scheme']}://{$url['user']}";
- if(isset($url['pass'])){
- $connection_string = "{$connection_string}:{$url['pass']}";
- }
- $connection_string = "{$connection_string}@{$url['host']}:$port{$url['path']}";
+ $info->port = $port;
+
+ if ($this->conn->adapter != 'sqlite')
+ ActiveRecord\Connection::instance($info);
+ }
+
+ public function test_connect_with_array_connection_info()
+ {
+ $config = ActiveRecord\Config::instance();
+ $info = $config->get_default_connection_info();
+
+ $info_array = array_filter(get_object_vars($info));
- if ($this->conn->protocol != 'sqlite')
- ActiveRecord\Connection::instance($connection_string);
+ if ($this->conn->adapter != 'sqlite')
+ ActiveRecord\Connection::instance($info_array);
}
/**
@@ -104,7 +111,7 @@ public function test_connect_with_port()
*/
public function test_connect_to_invalid_database()
{
- ActiveRecord\Connection::instance("{$this->conn->protocol}://test:test@127.0.0.1/" . self::InvalidDb);
+ ActiveRecord\Connection::instance("{$this->conn->adapter}://test:test@127.0.0.1/" . self::InvalidDb);
}
public function test_date_time_type()
@@ -408,4 +415,3 @@ public function test_date_to_string()
$this->assert_equals($datetime,$this->conn->date_to_string(date_create($datetime)));
}
}
-?>
diff --git a/test/helpers/DatabaseLoader.php b/test/helpers/DatabaseLoader.php
index 8449e1bb2..e478d7e1b 100644
--- a/test/helpers/DatabaseLoader.php
+++ b/test/helpers/DatabaseLoader.php
@@ -8,14 +8,14 @@ public function __construct($db)
{
$this->db = $db;
- if (!isset(static::$instances[$db->protocol]))
- static::$instances[$db->protocol] = 0;
+ if (!isset(static::$instances[$db->adapter]))
+ static::$instances[$db->adapter] = 0;
- if (static::$instances[$db->protocol]++ == 0)
+ if (static::$instances[$db->adapter]++ == 0)
{
// drop and re-create the tables one time only
$this->drop_tables();
- $this->exec_sql_script($db->protocol);
+ $this->exec_sql_script($db->adapter);
}
}
@@ -23,14 +23,14 @@ public function reset_table_data()
{
foreach ($this->get_fixture_tables() as $table)
{
- if ($this->db->protocol == 'oci' && $table == 'rm-bldg')
+ if ($this->db->adapter == 'oci' && $table == 'rm-bldg')
continue;
$this->db->query('DELETE FROM ' . $this->quote_name($table));
$this->load_fixture_data($table);
}
- $after_fixtures = $this->db->protocol.'-after-fixtures';
+ $after_fixtures = $this->db->adapter.'-after-fixtures';
try {
$this->exec_sql_script($after_fixtures);
} catch (Exception $e) {
@@ -44,7 +44,7 @@ public function drop_tables()
foreach ($this->get_fixture_tables() as $table)
{
- if ($this->db->protocol == 'oci')
+ if ($this->db->adapter == 'oci')
{
$table = strtoupper($table);
@@ -55,7 +55,7 @@ public function drop_tables()
if (in_array($table,$tables))
$this->db->query('DROP TABLE ' . $this->quote_name($table));
- if ($this->db->protocol == 'oci')
+ if ($this->db->adapter == 'oci')
{
try {
$this->db->query("DROP SEQUENCE {$table}_seq");
@@ -121,10 +121,9 @@ public function load_fixture_data($table)
public function quote_name($name)
{
- if ($this->db->protocol == 'oci')
+ if ($this->db->adapter == 'oci')
$name = strtoupper($name);
return $this->db->quote_name($name);
}
}
-?>
diff --git a/test/helpers/DatabaseTest.php b/test/helpers/DatabaseTest.php
index c9c29045d..a597b768d 100644
--- a/test/helpers/DatabaseTest.php
+++ b/test/helpers/DatabaseTest.php
@@ -22,7 +22,8 @@ public function set_up($connection_name=null)
if ($connection_name == 'sqlite' || $config->get_default_connection() == 'sqlite')
{
// need to create the db. the adapter specifically does not create it for us.
- static::$db = substr(ActiveRecord\Config::instance()->get_connection('sqlite'),9);
+ $info = ActiveRecord\Config::instance()->get_connection_info('sqlite');
+ static::$db = $info->host;
new SQLite3(static::$db);
}
@@ -82,4 +83,3 @@ public function assert_sql_doesnt_has($needle, $haystack)
return $this->assertNotContains($needle, $haystack);
}
}
-?>
diff --git a/test/helpers/SnakeCase_PHPUnit_Framework_TestCase.php b/test/helpers/SnakeCase_PHPUnit_Framework_TestCase.php
index a5ac79c85..1016aa044 100644
--- a/test/helpers/SnakeCase_PHPUnit_Framework_TestCase.php
+++ b/test/helpers/SnakeCase_PHPUnit_Framework_TestCase.php
@@ -61,4 +61,3 @@ public function assert_datetime_equals($expected, $actual)
$this->assert_equals($expected->format(DateTime::ISO8601),$actual->format(DateTime::ISO8601));
}
}
-?>
\ No newline at end of file
diff --git a/test/helpers/config.php b/test/helpers/config.php
index 49a488660..a71243e14 100644
--- a/test/helpers/config.php
+++ b/test/helpers/config.php
@@ -82,4 +82,3 @@
});
error_reporting(E_ALL | E_STRICT);
-?>
\ No newline at end of file
diff --git a/test/helpers/foo.php b/test/helpers/foo.php
index 1ed77ca51..49e3194eb 100644
--- a/test/helpers/foo.php
+++ b/test/helpers/foo.php
@@ -23,6 +23,3 @@ class UserNewsletter extends \ActiveRecord\Model {
array('newsletter'),
);
}
-
-# vim: ts=4 noet nobinary
-?>
diff --git a/test/models/Amenity.php b/test/models/Amenity.php
index ce6e3f163..70f6e442b 100644
--- a/test/models/Amenity.php
+++ b/test/models/Amenity.php
@@ -7,5 +7,4 @@ class Amenity extends ActiveRecord\Model
static $has_many = array(
'property_amenities'
);
-};
-?>
+}
diff --git a/test/models/Author.php b/test/models/Author.php
index 321ffe9d1..16987facd 100644
--- a/test/models/Author.php
+++ b/test/models/Author.php
@@ -25,5 +25,4 @@ public function return_something()
{
return array("sharks" => "lasers");
}
-};
-?>
+}
diff --git a/test/models/AuthorAttrAccessible.php b/test/models/AuthorAttrAccessible.php
index 1443629c7..fb870c486 100644
--- a/test/models/AuthorAttrAccessible.php
+++ b/test/models/AuthorAttrAccessible.php
@@ -13,5 +13,4 @@ class AuthorAttrAccessible extends ActiveRecord\Model
// No attributes should be accessible
static $attr_accessible = array(null);
-};
-?>
+}
diff --git a/test/models/AwesomePerson.php b/test/models/AwesomePerson.php
index 33aeece65..3bf9d8b07 100644
--- a/test/models/AwesomePerson.php
+++ b/test/models/AwesomePerson.php
@@ -3,4 +3,3 @@ class AwesomePerson extends ActiveRecord\Model
{
static $belongs_to = array('author');
}
-?>
diff --git a/test/models/Book.php b/test/models/Book.php
index 88e35d403..d001b37d3 100644
--- a/test/models/Book.php
+++ b/test/models/Book.php
@@ -32,5 +32,4 @@ public function get_lower_name()
{
return strtolower($this->name);
}
-};
-?>
+}
diff --git a/test/models/BookAttrAccessible.php b/test/models/BookAttrAccessible.php
index 59d1efbcc..f9204d6fd 100644
--- a/test/models/BookAttrAccessible.php
+++ b/test/models/BookAttrAccessible.php
@@ -6,5 +6,4 @@ class BookAttrAccessible extends ActiveRecord\Model
static $attr_accessible = array('author_id');
static $attr_protected = array('book_id');
-};
-?>
\ No newline at end of file
+}
diff --git a/test/models/BookAttrProtected.php b/test/models/BookAttrProtected.php
index ab1305009..876925f9d 100644
--- a/test/models/BookAttrProtected.php
+++ b/test/models/BookAttrProtected.php
@@ -9,5 +9,5 @@ class BookAttrProtected extends ActiveRecord\Model
// No attributes should be accessible
static $attr_accessible = array(null);
-};
-?>
+}
+
diff --git a/test/models/Employee.php b/test/models/Employee.php
index dfdf2ccf6..606ccb6ad 100644
--- a/test/models/Employee.php
+++ b/test/models/Employee.php
@@ -2,5 +2,4 @@
class Employee extends ActiveRecord\Model
{
static $has_one;
-};
-?>
\ No newline at end of file
+}
diff --git a/test/models/Event.php b/test/models/Event.php
index ca3c13d74..5243cbda3 100644
--- a/test/models/Event.php
+++ b/test/models/Event.php
@@ -10,5 +10,5 @@ class Event extends ActiveRecord\Model
array('state', 'address', 'to' => 'venue'),
array('name', 'to' => 'host', 'prefix' => 'woot')
);
-};
-?>
+}
+
diff --git a/test/models/Host.php b/test/models/Host.php
index 7c66e3f08..fcc6a9626 100644
--- a/test/models/Host.php
+++ b/test/models/Host.php
@@ -6,4 +6,3 @@ class Host extends ActiveRecord\Model
array('venues', 'through' => 'events')
);
}
-?>
diff --git a/test/models/JoinAuthor.php b/test/models/JoinAuthor.php
index d39fb873a..f2b250c43 100644
--- a/test/models/JoinAuthor.php
+++ b/test/models/JoinAuthor.php
@@ -3,5 +3,4 @@ class JoinAuthor extends ActiveRecord\Model
{
static $table_name = 'authors';
static $pk = 'author_id';
-};
-?>
\ No newline at end of file
+}
diff --git a/test/models/JoinBook.php b/test/models/JoinBook.php
index be845534a..2b1199db5 100644
--- a/test/models/JoinBook.php
+++ b/test/models/JoinBook.php
@@ -4,5 +4,4 @@ class JoinBook extends ActiveRecord\Model
static $table_name = 'books';
static $belongs_to = array();
-};
-?>
\ No newline at end of file
+}
diff --git a/test/models/NamespaceTest/Book.php b/test/models/NamespaceTest/Book.php
index f2e237e5b..ee5456628 100644
--- a/test/models/NamespaceTest/Book.php
+++ b/test/models/NamespaceTest/Book.php
@@ -14,4 +14,3 @@ class Book extends \ActiveRecord\Model
array('pages_2', 'class_name' => 'SubNamespaceTest\Page'),
);
}
-?>
diff --git a/test/models/NamespaceTest/SubNamespaceTest/Page.php b/test/models/NamespaceTest/SubNamespaceTest/Page.php
index 2d59f2c65..c0cd7f07e 100644
--- a/test/models/NamespaceTest/SubNamespaceTest/Page.php
+++ b/test/models/NamespaceTest/SubNamespaceTest/Page.php
@@ -7,4 +7,3 @@ class Page extends \ActiveRecord\Model
array('book', 'class_name' => '\NamespaceTest\Book'),
);
}
-?>
diff --git a/test/models/Position.php b/test/models/Position.php
index ce409d815..9d98089c2 100644
--- a/test/models/Position.php
+++ b/test/models/Position.php
@@ -2,5 +2,4 @@
class Position extends ActiveRecord\Model
{
static $belongs_to;
-};
-?>
\ No newline at end of file
+}
diff --git a/test/models/Property.php b/test/models/Property.php
index e0f913cc9..9205c96d6 100644
--- a/test/models/Property.php
+++ b/test/models/Property.php
@@ -8,5 +8,4 @@ class Property extends ActiveRecord\Model
'property_amenities',
array('amenities', 'through' => 'property_amenities')
);
-};
-?>
+}
diff --git a/test/models/PropertyAmenity.php b/test/models/PropertyAmenity.php
index 47f80e966..6ab72f4b2 100644
--- a/test/models/PropertyAmenity.php
+++ b/test/models/PropertyAmenity.php
@@ -8,5 +8,4 @@ class PropertyAmenity extends ActiveRecord\Model
'amenity',
'property'
);
-};
-?>
+}
diff --git a/test/models/RmBldg.php b/test/models/RmBldg.php
index fbc9ce79d..b6fb38006 100644
--- a/test/models/RmBldg.php
+++ b/test/models/RmBldg.php
@@ -1,33 +1,32 @@
- 'is missing!@#'),
- array('rm_name')
- );
-
- static $validates_length_of = array(
- array('space_out', 'within' => array(1, 5)),
- array('space_out', 'minimum' => 9, 'too_short' => 'var is too short!! it should be at least %d long')
- );
-
- static $validates_inclusion_of = array(
- array('space_out', 'in' => array('jpg', 'gif', 'png'), 'message' => 'extension %s is not included in the list'),
- );
-
- static $validates_exclusion_of = array(
- array('space_out', 'in' => array('jpeg'))
- );
-
- static $validates_format_of = array(
- array('space_out', 'with' => '/\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i' )
- );
-
- static $validates_numericality_of = array(
- array('space_out', 'less_than' => 9, 'greater_than' => '5'),
- array('rm_id', 'less_than' => 10, 'odd' => null)
- );
-}
-?>
+ 'is missing!@#'),
+ array('rm_name')
+ );
+
+ static $validates_length_of = array(
+ array('space_out', 'within' => array(1, 5)),
+ array('space_out', 'minimum' => 9, 'too_short' => 'var is too short!! it should be at least %d long')
+ );
+
+ static $validates_inclusion_of = array(
+ array('space_out', 'in' => array('jpg', 'gif', 'png'), 'message' => 'extension %s is not included in the list'),
+ );
+
+ static $validates_exclusion_of = array(
+ array('space_out', 'in' => array('jpeg'))
+ );
+
+ static $validates_format_of = array(
+ array('space_out', 'with' => '/\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i' )
+ );
+
+ static $validates_numericality_of = array(
+ array('space_out', 'less_than' => 9, 'greater_than' => '5'),
+ array('rm_id', 'less_than' => 10, 'odd' => null)
+ );
+}
diff --git a/test/models/Venue.php b/test/models/Venue.php
index 38ad9241b..c1540375b 100644
--- a/test/models/Venue.php
+++ b/test/models/Venue.php
@@ -4,6 +4,7 @@ class Venue extends ActiveRecord\Model
static $use_custom_get_state_getter = false;
static $use_custom_set_state_setter = false;
+ static $use_custom_set_city_setter = false;
static $has_many = array(
'events',
@@ -32,6 +33,13 @@ public function set_state($value)
else
return $this->assign_attribute('state', $value);
}
+
+ public function set_city($value)
+ {
+ if (self::$use_custom_set_city_setter)
+ return $this->assign_attribute('city', $value . '#');
+ else
+ return $this->assign_attribute('city', $value);
+ }
-};
-?>
+}
diff --git a/test/models/VenueCB.php b/test/models/VenueCB.php
index 7d96f2427..32255881a 100644
--- a/test/models/VenueCB.php
+++ b/test/models/VenueCB.php
@@ -41,4 +41,3 @@ public function before_validation_halt_execution()
return false;
}
}
-?>
\ No newline at end of file
diff --git a/test/sql/mysql.sql b/test/sql/mysql.sql
index 60e529bc6..90dd82242 100644
--- a/test/sql/mysql.sql
+++ b/test/sql/mysql.sql
@@ -92,7 +92,8 @@ CREATE TABLE property_amenities(
);
CREATE TABLE users (
- id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
+ id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ password VARCHAR(255) NOT NULL
) ENGINE=InnoDB;
CREATE TABLE newsletters (
@@ -109,4 +110,4 @@ CREATE TABLE valuestore (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`key` varchar(20) NOT NULL DEFAULT '',
`value` varchar(255) NOT NULL DEFAULT ''
-) ENGINE=InnoDB;
\ No newline at end of file
+) ENGINE=InnoDB;
diff --git a/test/sql/pgsql.sql b/test/sql/pgsql.sql
index edda21fae..fdc91dd80 100644
--- a/test/sql/pgsql.sql
+++ b/test/sql/pgsql.sql
@@ -91,7 +91,8 @@ CREATE TABLE property_amenities(
);
CREATE TABLE users(
- id serial primary key
+ id serial primary key,
+ password varchar(255) not null
);
CREATE TABLE newsletters(
@@ -104,6 +105,17 @@ CREATE TABLE user_newsletters(
newsletter_id int not null
);
+CREATE TABLE services(
+ id serial primary key,
+ code varchar(255) not null
+);
+
+CREATE TABLE active_services (
+ id serial primary key,
+ user_id int not null,
+ serv_id int not null
+);
+
CREATE TABLE valuestore (
id serial primary key,
key varchar(20) NOT NULL DEFAULT '',
diff --git a/test/sql/sqlite.sql b/test/sql/sqlite.sql
index b5abfeda0..35efdc6bf 100644
--- a/test/sql/sqlite.sql
+++ b/test/sql/sqlite.sql
@@ -91,7 +91,8 @@ CREATE TABLE property_amenities(
);
CREATE TABLE users (
- id INTEGER NOT NULL PRIMARY KEY
+ id INTEGER NOT NULL PRIMARY KEY,
+ password VARCHAR(255) NOT NULL
);
CREATE TABLE newsletters (