Ruby on Rails: new vs. create vs. build difference

27
Oct
5

Difference by examples. Let’s say that a User has_many Blogs and a Blog belongs_to a User.

$ ruby -v
ruby 1.8.7 (2008-08-11 patchlevel 72) [i686-linux]
$ rails -v
Rails 2.3.4

Last updated: 27.10.2009

new

New objects can be instantiated as either empty (pass no construction parameter) or pre-set with attributes but not yet saved (pass a hash with key names matching the associated table column names). In both instances, valid attribute keys are determined by the column names of the associated table — hence you can‘t have attributes that aren‘t part of the table columns.

Model.new

Requires additional save to save to database.

>> b = Blog.new({:title => "Test"})
+-------+---------+------------+------------+
| title | user_id | created_at | updated_at |
+-------+---------+------------+------------+
| Test  | 1       |            |            |
+-------+---------+------------+------------+
1 row in set
>> b.save
  SQL (0.1ms)   BEGIN
  Blog Create (0.3ms)   INSERT INTO `blogs` (`created_at`, `title`, `updated_at`, `user_id`) VALUES('2009-10-27 04:35:20', 'Test', '2009-10-27 04:35:20', 1)
  SQL (1.9ms)   COMMIT
=> true

New and associating through <<

The object is saved to the database after adding the association using <<.

>> u = User.first
>> b = Blog.new({:title => "Test"})
+-------+---------+------------+------------+
| title | user_id | created_at | updated_at |
+-------+---------+------------+------------+
| Test  | 1       |            |            |
+-------+---------+------------+------------+
1 row in set
>> u.blogs << b
  SQL (0.1ms)   BEGIN
  Blog Create (0.4ms)   INSERT INTO `blogs` (`created_at`, `title`, `updated_at`, `user_id`) VALUES('2009-10-27 04:41:15', 'Test', '2009-10-27 04:41:15', 1)
  SQL (11.6ms)   COMMIT
  Blog Load (0.4ms)   SELECT * FROM `blogs` WHERE (`blogs`.user_id = 1)
+----+---------+---------+---------+---------+
| id | title   | user_id | crea... | upda... |
+----+---------+---------+---------+---------+
| 6  | Test    | 1       | 2009... | 2009... |
+----+---------+---------+---------+---------+
1 row in set

Calling new on an association

When calling new on an association, it will create the object with the foregin key correctly set, but will not save it to the database (not even when parent is saved). The created object must be saved manually with save.

>> u = User.first
>> b = u.blogs.new({:title => "Test"})
+-------+---------+------------+------------+
| title | user_id | created_at | updated_at |
+-------+---------+------------+------------+
| Test  | 1       |            |            |
+-------+---------+------------+------------+
1 row in set
>> u.save
  SQL (0.1ms)   BEGIN
  SQL (0.2ms)   COMMIT
=> true
>> u.blogs.all
  Blog Load (0.1ms)   SELECT * FROM `blogs` WHERE (`blogs`.user_id = 1)
0 rows in set
>> b.save
  SQL (0.1ms)   BEGIN
  Blog Create (0.3ms)   INSERT INTO `blogs` (`created_at`, `title`, `updated_at`, `user_id`) VALUES('2009-10-27 04:54:06', 'Test', '2009-10-27 04:54:06', 1)
  SQL (3.9ms)   COMMIT
=> true
>> u.blogs.all
  Blog Load (0.3ms)   SELECT * FROM `blogs` WHERE (`blogs`.user_id = 1)
+----+---------+---------+---------+---------+
| id | title   | user_id | crea... | upda... |
+----+---------+---------+---------+---------+
| 7  | Test    | 1       | 2009... | 2009... |
+----+---------+---------+---------+---------+
1 row in set

Build

build(associations, parent = nil)

Model.build

Build cannot be called directly on the model. It can only be called on associations.

>> Blog.build({:title => "Test"})
NoMethodError:   SQL (9.0ms)   SHOW TABLES
undefined method `build' for #

Calling build on an association

When calling build on an association, the new object will be saved when the parent is saved.

>> u = User.first
>> u.blogs.build({:title => "Test"})
+-------+---------+------------+------------+
| title | user_id | created_at | updated_at |
+-------+---------+------------+------------+
| Test  | 1       |            |            |
+-------+---------+------------+------------+
1 row in set
>> u.save
  SQL (0.1ms)   BEGIN
  Blog Create (0.3ms)   INSERT INTO `blogs` (`created_at`, `title`, `updated_at`, `user_id`) VALUES('2009-10-27 04:56:10', 'Test', '2009-10-27 04:56:10', 1)
  SQL (2.6ms)   COMMIT
=> true
>> u.blogs.all
  Blog Load (0.3ms)   SELECT * FROM `blogs` WHERE (`blogs`.user_id = 1)
+----+---------+---------+---------+---------+
| id | title   | user_id | crea... | upda... |
+----+---------+---------+---------+---------+
| 7  | Test    | 1       | 2009... | 2009... |
+----+---------+---------+---------+---------+
1 row in set

Create

Creates an object (or multiple objects) and saves it to the database, if validations pass. The resulting object is returned whether the object was saved successfully to the database or not.

Model.create

As the description of the method says, the object will be saved to the database if it passes validations.

>> u = User.first
>> Blog.create({:title => "Test"})
  SQL (0.1ms)   BEGIN
  Blog Create (0.3ms)   INSERT INTO `blogs` (`created_at`, `title`, `updated_at`, `user_id`) VALUES('2009-10-27 05:06:55', 'Test', '2009-10-27 05:06:55', NULL)
  SQL (10.7ms)   COMMIT
+----+-------+---------+-----------+-----------+
| id | title | user_id | create... | update... |
+----+-------+---------+-----------+-----------+
| 9  | Test  |         | 2009-1... | 2009-1... |
+----+-------+---------+-----------+-----------+
1 row in set
>> Blog.all
  Blog Load (0.3ms)   SELECT * FROM `blogs`
+----+-------+---------+-----------+-----------+
| id | title | user_id | create... | update... |
+----+-------+---------+-----------+-----------+
| 9  | Test  |         | 2009-1... | 2009-1... |
+----+-------+---------+-----------+-----------+
1 row in set

Note: In this case, there is no validation that the user_id field must be present, so such an object passes validation. If validation was added for user_id, the row would not be created in this case, because the object would not have passed validation. In that case, :user_id would have to be explicitly passed in the attrbiutes hash.

Calling create on an association

Works similar to build in this case (automatically adds the foregin_key - user_id in this case), but the new row is saved to the database already, and not only after the parent model is saved (as is the case with build).

>> u = User.first
>> u.blogs.create({:title => "Test"})
  SQL (0.1ms)   BEGIN
  Blog Create (0.3ms)   INSERT INTO `blogs` (`created_at`, `title`, `updated_at`, `user_id`) VALUES('2009-10-27 05:11:22', 'Test', '2009-10-27 05:11:22', 1)
  SQL (0.7ms)   COMMIT
+----+-------+---------+-----------+-----------+
| id | title | user_id | create... | update... |
+----+-------+---------+-----------+-----------+
| 10 | Test  |         | 2009-1... | 2009-1... |
+----+-------+---------+-----------+-----------+
1 row in set
>> u.blogs.all
  Blog Load (0.3ms)   SELECT * FROM `blogs` WHERE (`blogs`.user_id = 1)
+----+-------+---------+-----------+-----------+
| id | title | user_id | create... | update... |
+----+-------+---------+-----------+-----------+
| 10 | Test  |         | 2009-1... | 2009-1... |
+----+-------+---------+-----------+-----------+
1 row in set
>> u.save
  SQL (0.1ms)   BEGIN
  SQL (0.2ms)   COMMIT
=> true

Cheat cheet

Steps required for the object to be saved to database.

New

  • Blog.new(…).save
  • user.blogs << Blog.new(…)
  • user.blogs.new(…).save – do not use, no practical use case

Build

  • Blog.build – not possible
  • user.blogs.build(…), user.save – both are required to save to DB

Create

  • Blog.create(…)
  • user.blogs.create(…)

New idea for Windows Touch Remix

20
May
3

I’m going to do a quick-dial feature for Windows Touch Remix. I want to keep the code nice so it’ll take me a few days to get there, but this is how it looks at the moment (and how it’s supposed to look, anyway):

wtr2

Personally i kind of like the shading, what do you think? :)

F#.NET

17
May
0

F#.NET is a new functional language from Microsoft. It looks very promising, and here you can see a very good video presentation Luca Bolognese gave about it.

Fibonacci function

Snippet of the day: C#.NET configuration files

17
May
0

I’ll start with a longer snippet – handling configuration files in C#.NET. I used this in Windows Touch Remix.

This is the base class I use for my configuration classes:

[Serializable()]
public class XmlConf
{
    public static T FromFile<T>(string filename)
    {
        if (!File.Exists(filename)) return default(T);
        XmlSerializer ser = new XmlSerializer(typeof(T));
        StreamReader fs = new StreamReader(filename);
        T t = (T) ser.Deserialize(fs);
        fs.Close();
        return t;
    }

    public void Save(string filename)
    {
        XmlSerializer ser = new XmlSerializer(this.GetType());
        StreamWriter fs = new StreamWriter(filename, false);
        ser.Serialize(fs, this);
        fs.Close();
    }
}

Just subclass it and add your configuration properties:

    [Serializable()]
    public class MyConf : XmlConf
    {
        public int IntVal { get; set; }
        public string StrVal { get; set; }
    }

Make sure you use getters and setters like in the above example, so the values get serialized properly. Most .NET classes (like List) are serializable, but if you have properties of your own class, make sure they are serializable.

Here’s a usage example of saving and loading the configuration:

// Save
MyConf conf = new MyConf();
conf.IntVal = 100;
conf.StrVal = "hey";
conf.Save("my.conf");

// Load
conf = MyConf.FromFile<MyConf>("my.conf");
if (conf == null) new Exception("Configuration file not found.");
Console.WriteLine(conf.IntVal);
Console.WriteLine(conf.StrVal);

Windows Touch Remix

17
May
1

I did some further work on my Windows Touch Remix project, which I’ll be presenting at the NTK (Microsoft NT Conference) in Portorož on the 25th (i think) of May.

Here is the screenshot of the current version:

wtr

These are some of the more important features:

- Can replace your desktop

- Works like a (hopefully) stylish Start Menu

- Can import apps simply by dragging them from Program Files

- Almost completely customizable

I’m also working on an online AppStore-thingy which would allow auto-configuration of apps, but more importantly provide nice big icons you can use.

If anyone knows how to make Windows not include those shortcut arrows in the icons if you’re extracting them from shortcuts, let me know (the problem is that the shortcut can be to a non-exe file or have a different icon than the exe does).

You can download WTR (Windows Touch Remix) here.

My article about new DLL injection and API hooking methods

15
May
10

I’m writing a ‘scientific’ article on DLL injection and API hooking (advanced programming techniques) for the Windows NT OS. Me and Zoran Bosnić (my menthor) are almost finished with it and it will hopefully be published in the SPE journal. I don’t know how long it takes to publish, but I hope it will be confirmed by August. Does anyone know?

Also, any idea if you can publish such an article on your own site, or have you basically given those rights away to the publisher?

A brief explanation of new methods which I’ve developed:

- DLL injection: I use debugger API in a similar manner that the CreateRemoteThread approach uses, but I execute the code via modification of the main thread’s context to run the code for me (instead of creating a new thread). This approach seems to be somewhat slower, but that is not important. What is important that it allows DLL injection into a suspended process or in other words injection will work even if you create a process in a suspended state (unlike CreateRemoteThread which does not work in this case).

- API hooking: I’ve developed a method of API hooking that is able to hook any single machine code instruction, which might be useful in some cases. Also, it allows for hooking of instructions that contain relative memory addresses, unlike Microsoft Detours. I hope it will be possible to further optimize this method, at it is considerably slow compared to Detours. However when the Detours approach is applicable I just use it instead, but I’d still like to make it faster for cases which Detours can not handle. Code redirection time with Detours: 1ns, my method: 1600ms. Wow, that really is much slower. But at least it works in such cases where Detours fails.