hobofields 0.7.5

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES.txt ADDED
@@ -0,0 +1,38 @@
1
+ == HoboFields 0.7.4 ==
2
+
3
+ Fix to using the migration generator with STI
4
+
5
+ HoboFields::Text - fix to to_html
6
+
7
+ Ensure that the bundled rich types are only loaded on demand
8
+
9
+ HoboFields::TextileString -- adding require 'redcloth'
10
+
11
+ Fix for HoboFields::EnumString -- couldn't set values using
12
+ symbols
13
+
14
+ Adding to_html to PasswordString (returns '[password-hidden]')
15
+
16
+ EnumString -- now defines constants on the class for each value in
17
+ the enum
18
+
19
+ Also, won't define an is_foo? method if that already exists
20
+
21
+ Removing HoboFields::Percentage -- this is better handled as an
22
+ application specific type
23
+
24
+ For example, is a percentage an fixnum or a float? Is 101% valid
25
+ or invalid?
26
+
27
+ Fix to validates_virtual_field
28
+
29
+ *Breaking Change* EnumString: Change to automatically defined
30
+ class methods on EnumString classes
31
+
32
+ It used to be that if you did EnumString.for(:foo, :baa), your
33
+ class had methods foo, baa and the instance got methods foo?
34
+ and baa?. There was a big problem with name clashes with other
35
+ class methods. You now get no class methods, and the instance
36
+ methods are called is_foo? and is_baa?.
37
+
38
+ Rich field type fix: don't attempt to wrap booleans
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2008 Tom Locke
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
data/Manifest ADDED
@@ -0,0 +1,25 @@
1
+ CHANGES.txt
2
+ generators/hobo_migration/hobo_migration_generator.rb
3
+ generators/hobo_migration/templates/migration.rb
4
+ init.rb
5
+ lib/hobo_fields/email_address.rb
6
+ lib/hobo_fields/enum_string.rb
7
+ lib/hobo_fields/field_declaration_dsl.rb
8
+ lib/hobo_fields/field_spec.rb
9
+ lib/hobo_fields/fields_declaration.rb
10
+ lib/hobo_fields/html_string.rb
11
+ lib/hobo_fields/markdown_string.rb
12
+ lib/hobo_fields/migration_generator.rb
13
+ lib/hobo_fields/model_extensions.rb
14
+ lib/hobo_fields/password_string.rb
15
+ lib/hobo_fields/text.rb
16
+ lib/hobo_fields/textile_string.rb
17
+ lib/hobo_fields.rb
18
+ lib/hobofields.rb
19
+ LICENSE.txt
20
+ README.txt
21
+ test/hobofields.rdoctest
22
+ test/hobofields_api.rdoctest
23
+ test/migration_generator.rdoctest
24
+ test/rich_types.rdoctest
25
+ Manifest
data/README.txt ADDED
@@ -0,0 +1,8 @@
1
+ = HoboFields
2
+
3
+ Rich field types and migration-generator for Rails.
4
+
5
+ Part of the Hobo project
6
+
7
+ http://hobocentral.net/hobofields
8
+
@@ -0,0 +1,95 @@
1
+ class HoboMigrationGenerator < Rails::Generator::Base
2
+
3
+ def initialize(runtime_args, runtime_options = {})
4
+ super
5
+ @migration_name = runtime_args.first || begin
6
+ i = Dir["#{RAILS_ROOT}/db/migrate/*hobo_migration*"].length
7
+ "hobo_migration_#{i+1}"
8
+ end
9
+ end
10
+
11
+ def manifest
12
+ generator = HoboFields::MigrationGenerator.new(self)
13
+ up, down = generator.generate
14
+
15
+ if up.blank?
16
+ puts "Database and models match -- nothing to change"
17
+ return record {|m| }
18
+ end
19
+
20
+ puts "\n---------- Up Migration ----------", up, "----------------------------------"
21
+ puts "\n---------- Down Migration --------", down, "----------------------------------"
22
+
23
+ action = input("What now: [g]enerate migration, generate and [m]igrate now or [c]ancel?", %w(g m c))
24
+
25
+ if action == 'c'
26
+ # record nothing to keep the generator happy
27
+ record {|m| }
28
+ else
29
+ puts "\nMigration filename:", "(you can type spaces instead of '_' -- every little helps)"
30
+ migration_name = input("Filename [#@migration_name]:").strip.gsub(' ', '_')
31
+ migration_name = @migration_name if migration_name.blank?
32
+
33
+ at_exit { system "rake db:migrate" } if action == 'm'
34
+
35
+ up.gsub!("\n", "\n ")
36
+ down.gsub!("\n", "\n ")
37
+
38
+ record do |m|
39
+ m.migration_template 'migration.rb', 'db/migrate',
40
+ :assigns => { :up => up, :down => down, :migration_name => migration_name.camelize },
41
+ :migration_file_name => migration_name
42
+ end
43
+ end
44
+ rescue HoboFields::FieldSpec::UnknownSqlTypeError => e
45
+ puts "Invalid field type '#{e.message[2]}' for #{e.message[0]}.#{e.message[1]}"
46
+ record {|m| }
47
+ end
48
+
49
+
50
+ def extract_renames!(to_create, to_drop, kind_str, name_prefix="")
51
+ to_rename = {}
52
+ rename_to_choices = to_create
53
+ to_drop.dup.each do |t|
54
+ if rename_to_choices.empty?
55
+ puts "\nCONFIRM DROP! #{kind_str} #{name_prefix}#{t}"
56
+ resp = input("Enter 'drop #{t}' to confirm:")
57
+ if resp.strip != "drop " + t.to_s
58
+ to_drop.delete(t)
59
+ end
60
+ else
61
+ puts "\nDROP or RENAME?: #{kind_str} #{name_prefix}#{t}"
62
+ puts "Rename choices: #{to_create * ', '}"
63
+ resp = input("Enter either 'drop #{t}' or one of the rename choices:")
64
+ resp.strip!
65
+
66
+ if resp == "drop " + t
67
+ # Leave things as they are
68
+ else
69
+ to_drop.delete(t)
70
+ if resp.in?(rename_to_choices)
71
+ to_rename[t] = resp
72
+ to_create.delete(resp)
73
+ rename_to_choices.delete(resp)
74
+ end
75
+ end
76
+ end
77
+ end
78
+ to_rename
79
+ end
80
+
81
+
82
+ def input(prompt, options=nil)
83
+ print(prompt + " ")
84
+ if options
85
+ while !(response = STDIN.readline.strip.downcase).in?(options);
86
+ print(prompt + " ")
87
+ end
88
+ response
89
+ else
90
+ STDIN.readline
91
+ end
92
+ end
93
+
94
+ end
95
+
@@ -0,0 +1,9 @@
1
+ class <%= migration_name %> < ActiveRecord::Migration
2
+ def self.up
3
+ <%= up %>
4
+ end
5
+
6
+ def self.down
7
+ <%= down %>
8
+ end
9
+ end
@@ -0,0 +1,42 @@
1
+
2
+ # Gem::Specification for Hobofields-0.7.5
3
+ # Originally generated by Echoe
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = %q{hobofields}
7
+ s.version = "0.7.5"
8
+
9
+ s.specification_version = 2 if s.respond_to? :specification_version=
10
+
11
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
+ s.authors = ["Tom Locke"]
13
+ s.date = %q{2008-04-18}
14
+ s.description = %q{Rich field types and migration generator for Rails}
15
+ s.email = %q{tom@tomlocke.com}
16
+ s.extra_rdoc_files = ["lib/hobo_fields/email_address.rb", "lib/hobo_fields/enum_string.rb", "lib/hobo_fields/field_declaration_dsl.rb", "lib/hobo_fields/field_spec.rb", "lib/hobo_fields/fields_declaration.rb", "lib/hobo_fields/html_string.rb", "lib/hobo_fields/markdown_string.rb", "lib/hobo_fields/migration_generator.rb", "lib/hobo_fields/model_extensions.rb", "lib/hobo_fields/password_string.rb", "lib/hobo_fields/text.rb", "lib/hobo_fields/textile_string.rb", "lib/hobo_fields.rb", "lib/hobofields.rb", "LICENSE.txt", "README.txt"]
17
+ s.files = ["CHANGES.txt", "generators/hobo_migration/hobo_migration_generator.rb", "generators/hobo_migration/templates/migration.rb", "init.rb", "lib/hobo_fields/email_address.rb", "lib/hobo_fields/enum_string.rb", "lib/hobo_fields/field_declaration_dsl.rb", "lib/hobo_fields/field_spec.rb", "lib/hobo_fields/fields_declaration.rb", "lib/hobo_fields/html_string.rb", "lib/hobo_fields/markdown_string.rb", "lib/hobo_fields/migration_generator.rb", "lib/hobo_fields/model_extensions.rb", "lib/hobo_fields/password_string.rb", "lib/hobo_fields/text.rb", "lib/hobo_fields/textile_string.rb", "lib/hobo_fields.rb", "lib/hobofields.rb", "LICENSE.txt", "README.txt", "test/hobofields.rdoctest", "test/hobofields_api.rdoctest", "test/migration_generator.rdoctest", "test/rich_types.rdoctest", "Manifest", "hobofields.gemspec"]
18
+ s.has_rdoc = true
19
+ s.homepage = %q{http://hobocentral.net/hobofields}
20
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Hobofields", "--main", "README.txt"]
21
+ s.require_paths = ["lib"]
22
+ s.rubyforge_project = %q{hobo}
23
+ s.rubygems_version = %q{1.0.1}
24
+ s.summary = %q{Rich field types and migration generator for Rails}
25
+ end
26
+
27
+
28
+ # # Original Rakefile source (requires the Echoe gem):
29
+ #
30
+ # require 'echoe'
31
+ #
32
+ # Echoe.new('hobofields') do |p|
33
+ # p.author = "Tom Locke"
34
+ # p.email = "tom@tomlocke.com"
35
+ # p.summary = "Rich field types and migration generator for Rails"
36
+ # p.url = "http://hobocentral.net/hobofields"
37
+ # p.project = "hobo"
38
+ #
39
+ # p.changelog = "CHANGES.txt"
40
+ # p.version = "0.7.5"
41
+ # end
42
+ #
data/init.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'hobosupport'
2
+
3
+ # Use ActiveSupport to load if it hasn't been loaded already
4
+ HoboFields
@@ -0,0 +1,24 @@
1
+ module HoboFields
2
+
3
+ class EmailAddress < String
4
+
5
+ COLUMN_TYPE = :string
6
+
7
+ def validate
8
+ "is not valid" unless valid? || blank?
9
+ end
10
+
11
+ def valid?
12
+ self =~ /^\s*([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\s*$/i
13
+ end
14
+
15
+ def to_html
16
+ self
17
+ end
18
+
19
+ HoboFields.register_type(:email_address, self)
20
+
21
+ end
22
+
23
+ end
24
+
@@ -0,0 +1,64 @@
1
+ require 'hobo_fields/field_declaration_dsl'
2
+
3
+ module HoboFields
4
+
5
+ class EnumString < String
6
+
7
+ module DeclarationHelper
8
+
9
+ def enum_string(*values)
10
+ EnumString.for(*values)
11
+ end
12
+
13
+ end
14
+
15
+ FieldDeclarationDsl.send(:include, DeclarationHelper)
16
+
17
+
18
+ class << self
19
+
20
+ def with_values(*values)
21
+ @values = values.*.to_s
22
+ end
23
+
24
+ attr_accessor :values
25
+
26
+ def for(*values)
27
+ values = values.*.to_s
28
+ c = Class.new(EnumString) do
29
+ values.each do |v|
30
+ const_name = v.upcase
31
+ const_set(const_name, self.new(v)) unless const_defined?(const_name)
32
+
33
+ method_name = "is_#{v.underscore}?"
34
+ define_method(method_name) { self == v } unless self.respond_to?(method_name)
35
+ end
36
+ end
37
+ c.with_values(*values)
38
+ c
39
+ end
40
+
41
+ def inspect
42
+ name.blank? ? "#<EnumString #{(values || []) * ' '}>" : name
43
+ end
44
+ alias_method :to_s, :inspect
45
+
46
+ end
47
+
48
+ COLUMN_TYPE = :string
49
+
50
+ def validate
51
+ "must be one of #{self.class.values * ', '}" unless self.in?(self.class.values)
52
+ end
53
+
54
+ def ==(other)
55
+ if other.is_a?(Symbol)
56
+ super(other.to_s)
57
+ else
58
+ super
59
+ end
60
+ end
61
+
62
+ end
63
+
64
+ end
@@ -0,0 +1,29 @@
1
+ module HoboFields
2
+
3
+ class FieldDeclarationDsl < BlankSlate
4
+
5
+ def initialize(model)
6
+ @model = model
7
+ end
8
+
9
+ attr_reader :model
10
+
11
+
12
+ def timestamps
13
+ field(:created_at, :datetime)
14
+ field(:updated_at, :datetime)
15
+ end
16
+
17
+
18
+ def field(name, type, *args)
19
+ @model.declare_field(name, type, *args)
20
+ end
21
+
22
+
23
+ def method_missing(name, *args)
24
+ field(name, *args)
25
+ end
26
+
27
+ end
28
+
29
+ end
@@ -0,0 +1,72 @@
1
+ module HoboFields
2
+
3
+ class FieldSpec
4
+
5
+ class UnknownSqlTypeError < RuntimeError; end
6
+
7
+ def initialize(model, name, type, options={})
8
+ raise ArgumentError, "you cannot provide a field spec for the primary key" if name == model.primary_key
9
+ self.model = model
10
+ self.name = name.to_sym
11
+ self.type = type.is_a?(String) ? type.to_sym : type
12
+ self.options = options
13
+ self.position = model.field_specs.length
14
+ end
15
+
16
+ attr_accessor :model, :name, :type, :position, :options
17
+
18
+ def sql_type
19
+ options[:sql_type] or begin
20
+ if native_type?(type)
21
+ type
22
+ else
23
+ field_class = HoboFields.to_class(type)
24
+ field_class && field_class::COLUMN_TYPE or raise UnknownSqlTypeError, "#{model}.#{name}::#{type}"
25
+ end
26
+ end
27
+ end
28
+
29
+ def limit
30
+ options[:limit] || native_types[sql_type][:limit]
31
+ end
32
+
33
+ def precision
34
+ options[:precision]
35
+ end
36
+
37
+ def scale
38
+ options[:scale]
39
+ end
40
+
41
+ def null
42
+ :null.in?(options) ? options[:null] : true
43
+ end
44
+
45
+ def default
46
+ options[:default]
47
+ end
48
+
49
+ def different_to?(col_spec)
50
+ sql_type != col_spec.type ||
51
+ begin
52
+ check_cols = [:null, :default]
53
+ check_cols += [:precision, :scale] if sql_type == :decimal
54
+ check_cols << :limit if sql_type.in?([:string, :text, :binary, :integer])
55
+ check_cols.any? { |k| col_spec.send(k) != self.send(k) }
56
+ end
57
+ end
58
+
59
+
60
+ private
61
+
62
+ def native_type?(type)
63
+ type.in?(native_types.keys - [:primary_key])
64
+ end
65
+
66
+ def native_types
67
+ model.connection.native_database_types
68
+ end
69
+
70
+ end
71
+
72
+ end
@@ -0,0 +1,22 @@
1
+ module HoboFields
2
+
3
+ FieldsDeclaration = classy_module do
4
+
5
+ def self.fields(&b)
6
+ # Any model that calls 'fields' gets a bunch of other
7
+ # functionality included automatically, but make sure we only include it once
8
+ include HoboFields::ModelExtensions unless HoboFields::ModelExtensions.in?(included_modules)
9
+
10
+ if b
11
+ dsl = FieldDeclarationDsl.new(self)
12
+ if b.arity == 1
13
+ yield dsl
14
+ else
15
+ dsl.instance_eval(&b)
16
+ end
17
+ end
18
+ end
19
+
20
+ end
21
+
22
+ end
@@ -0,0 +1,15 @@
1
+ module HoboFields
2
+
3
+ class HtmlString < String
4
+
5
+ COLUMN_TYPE = :text
6
+
7
+ def to_html
8
+ self
9
+ end
10
+
11
+ HoboFields.register_type(:html, self)
12
+
13
+ end
14
+
15
+ end
@@ -0,0 +1,13 @@
1
+ module HoboFields
2
+
3
+ class MarkdownString < HoboFields::Text
4
+
5
+ HoboFields.register_type(:markdown, self)
6
+
7
+ def to_html
8
+ blank? ? "" : BlueCloth.new(self).to_html
9
+ end
10
+
11
+ end
12
+
13
+ end