metafy 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in metafy.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2004-2011 Caseproof, LLC
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
@@ -0,0 +1,82 @@
1
+ = Metafy
2
+
3
+ Metafy is a Gem for Rails 3 that makes it possible to easily add dynamic attributes to any ActiveRecord model. These lightweight, meta attributes work just like normal database column attributes on your ActiveRecord model and are fully searchable (i.e. you can find records by values contained in these dynamic attributes).
4
+
5
+ = Installation
6
+
7
+ To apply Metafy to any ActiveRecord model, follow these simple steps:
8
+
9
+ 1. Install
10
+ - Add to Gemfile: <b>gem 'metafy'</b>
11
+ - Install required gems: <b>bundle install</b>
12
+
13
+ 2. Add the 'mattrs' table to your database
14
+ - Create migration: <b>rails g metafy</b>
15
+ - Migrate your database: <b>rake db:migrate</b>
16
+
17
+ 3. Add metafied attributes to any of your ActiveRecord models using the <b>metafy</b> class method:
18
+ - Add to app/models/model.rb: <b>metafy :attribute_name</b>
19
+
20
+ = Example
21
+
22
+ Say you had a table named "users":
23
+
24
+ mysql> desc users;
25
+ +----------------+--------------+------+-----+---------+----------------+
26
+ | Field | Type | Null | Key | Default | Extra |
27
+ +----------------+--------------+------+-----+---------+----------------+
28
+ | id | int(11) | NO | PRI | NULL | auto_increment |
29
+ | username | varchar(255) | YES | MUL | NULL | |
30
+ | email | varchar(255) | YES | MUL | NULL | |
31
+ | created_at | datetime | YES | | NULL | |
32
+ | updated_at | datetime | YES | | NULL | |
33
+ +----------------+--------------+------+-----+---------+----------------+
34
+
35
+ And you wanted some lightweight, dynamic fields associated with the User model but didn't necessarily want them in the schema then you could add them as meta attributes using metafy like so:
36
+
37
+ # app/models/user.rb
38
+ class User < ActiveRecord::Base
39
+ metafy :first_name, :last_name,:gender, :country, :language
40
+ end
41
+
42
+ Now you can do things like:
43
+ >> user = User.meta_where( :first_name => 'Bevis' ).first
44
+ >> user.first_name = 'Larry'
45
+ >> user.save
46
+ >> user.first_name
47
+ => 'Larry'
48
+
49
+ OR create a new record with meta attributes:
50
+ >> user = User.new :first_name => 'Jerry', :last_name => 'Johnson'
51
+ >> user.save
52
+
53
+ OR select using the meta_where scope:
54
+ >> num_johnsons = User.meta_where( :last_name => 'Johnson' ).count
55
+
56
+ OR select & order using standard rails relations:
57
+ >> num_johnsons = User.where( :m_country => { :meta_value => 'Canada' } ).order( 'm_last_name.meta_value ASC' )
58
+
59
+ OR chain where with meta_where:
60
+ >> users = User.where( 'users.email LIKE "%@gmail.com"' ).meta_where( :language => "French" )
61
+
62
+ = Why would I want to use this Gem instead of just adding more database columns?
63
+ Well, there isn't much difference functionally between a normal database colunn and a meta attribute. But there may be sometimes when you want to add a more dynamic, lightweight attribute that doesn't clutter your schema. That's when you'd use metafy.
64
+
65
+ = Version history
66
+
67
+ - Version 0.0.5
68
+ - Added more documentation
69
+ - Version 0.0.4
70
+ - Fixed the metas table name issue in 3.0.7
71
+ - Version 0.0.3
72
+ - Added documentation
73
+ - Version 0.0.2
74
+ - Gem Fixes
75
+ - Version 0.0.1
76
+ - Initial Version
77
+
78
+ = Contact and copyright
79
+
80
+ Bug report? Faulty/incomplete documentation? Feature request? Please post an issue on 'http://github.com/Caseproof/metafy/issues'. If its urgent, please contact me from my website at 'http://blairwilliams.com/contact'
81
+
82
+ Copyright (c) 2004-2011 Caseproof, LLC, released under the MIT license
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,41 @@
1
+ class Mattr < ActiveRecord::Base
2
+ def self.meta(target_type, target_id, meta_key)
3
+ Mattr.where({ :target_type => target_type, :meta_key => meta_key, :target_id => target_id }).first
4
+ end
5
+
6
+ def self.meta=(args)
7
+ return false if args.nil? or !args.is_a?(Array) or args.length < 4
8
+ Mattr.metas = [ args[0], args[1], { args[2] => args[3] } ]
9
+ end
10
+
11
+ def self.metas=(args)
12
+ return if args.nil?
13
+ return unless args.is_a?(Array)
14
+ return unless args[2].is_a?(Hash)
15
+
16
+ args[2].each do |k,v|
17
+ unless meta = self.meta(args[0],args[1],k)
18
+ meta = Mattr.new
19
+ end
20
+
21
+ meta.target_type = args[0]
22
+ meta.target_id = args[1]
23
+ meta.meta_key = k
24
+ meta.meta_value = v
25
+ meta.save
26
+ end
27
+ end
28
+
29
+ def self.value(target_type, target_id, meta_key)
30
+ record = self.meta(target_type, target_id, meta_key)
31
+ record.meta_value unless record.nil?
32
+ end
33
+
34
+ def self.exists?(target_type, target_id, meta_key)
35
+ self.meta(target_type, target_id, meta_key) != false
36
+ end
37
+
38
+ def self.metas(target_type, target_id)
39
+ Mattr.where({ :target_type => target_type, :target_id => target_id })
40
+ end
41
+ end
@@ -0,0 +1,19 @@
1
+ require 'rails/generators'
2
+
3
+ class MetafyGenerator < Rails::Generators::Base
4
+ include Rails::Generators::Migration
5
+
6
+ source_root File.expand_path("../templates", __FILE__)
7
+
8
+ def self.next_migration_number(dirname)
9
+ if ActiveRecord::Base.timestamped_migrations
10
+ Time.now.utc.strftime("%Y%m%d%H%M%S")
11
+ else
12
+ "%.3d" % (current_migration_number(dirname) + 1)
13
+ end
14
+ end
15
+
16
+ def create_migration_file
17
+ migration_template 'create_mattrs.rb', 'db/migrate/create_mattrs.rb'
18
+ end
19
+ end
@@ -0,0 +1,20 @@
1
+ class CreateMattrs < ActiveRecord::Migration
2
+ def self.up
3
+ create_table(:mattrs) do |t|
4
+ t.string :meta_key, :null => false
5
+ t.text :meta_value
6
+ t.references :target, :polymorphic => true, :null => false
7
+
8
+ t.timestamps
9
+ end
10
+
11
+ add_index :mattrs, :meta_key
12
+ add_index :mattrs, :meta_value, :length => 255
13
+ add_index :mattrs, :target_id
14
+ add_index :mattrs, :target_type
15
+ end
16
+
17
+ def self.down
18
+ drop_table :mattrs
19
+ end
20
+ end
@@ -0,0 +1,4 @@
1
+ module Metafy
2
+ require 'metafy/engine' if defined?(Rails) && Rails::VERSION::MAJOR == 3
3
+ require 'metafy/activerecord_methods' if defined?(Rails) && Rails::VERSION::MAJOR == 3
4
+ end
@@ -0,0 +1,44 @@
1
+ require 'metafy/base'
2
+
3
+ # Adds the metafy method in ActiveRecord::Base, which can be used to define the metafied objects.
4
+ class << ActiveRecord::Base
5
+ def metafied?
6
+ @metafied || false
7
+ end
8
+
9
+ def metafy( *options )
10
+ cattr_accessor :metafied_attrs
11
+ self.metafied_attrs = options
12
+
13
+ # Sets to determine what classes have been metafied
14
+ @metafied = self.metafied_attrs.count > 0
15
+
16
+ meta_joins = []
17
+ class_name = self.name.classify
18
+ table_name = self.table_name
19
+ meta_selects = ["#{table_name}.*"]
20
+ self.metafied_attrs.each do |col|
21
+ meta_joins.push("LEFT JOIN mattrs m_#{col.to_s} ON m_#{col.to_s}.target_id=#{table_name}.id AND m_#{col.to_s}.meta_key='#{col.to_s}' AND m_#{col.to_s}.target_type='#{class_name}'")
22
+ meta_selects.push("m_#{col.to_s}.meta_value AS #{col.to_s}")
23
+ end
24
+
25
+ default_scope :select => meta_selects, :joins => meta_joins
26
+
27
+ scope :meta_where, lambda { |options|
28
+ conditions = {}
29
+ options.each_pair do |key,val|
30
+ conditions["m_#{key.to_s}".to_sym] = { :meta_value => val }
31
+ end
32
+
33
+ where(conditions)
34
+ }
35
+
36
+ has_many :mattr, :as => :target
37
+
38
+ options.each do | meta_attr |
39
+ attr_accessor meta_attr.to_sym
40
+ end
41
+
42
+ include Metafy::Base
43
+ end
44
+ end
@@ -0,0 +1,102 @@
1
+ module Metafy
2
+ module Base
3
+ def initialize(attributes = nil)
4
+ unless attributes.nil?
5
+ super(attributes.except(*self.metafied_attrs))
6
+ define_accessors_for_attributes
7
+ attributes.each do |n,v|
8
+ set_metafied_attribute(n,v) if has_metafied_attribute?(n)
9
+ end
10
+ else
11
+ super(nil)
12
+ end
13
+ end
14
+
15
+ def has_metafied_attribute?(attribute)
16
+ return self.metafied_attrs.include?(attribute.to_sym)
17
+ end
18
+
19
+ def read_metafied_attribute(attribute)
20
+ has_metafied_attribute?(attribute.to_sym) ? Mattr.value(self.class.name, self.id, attribute.to_s) : nil
21
+ end
22
+
23
+ def write_metafied_attributes
24
+ self.metafied_attrs.each do | meta_attr |
25
+ write_metafied_attribute( meta_attr )
26
+ end
27
+ end
28
+
29
+ def write_metafied_attribute( meta_attr )
30
+ meta_attr = meta_attr.to_sym
31
+
32
+ old_meta = read_metafied_attribute( meta_attr )
33
+ new_meta = get_metafied_attribute( meta_attr )
34
+
35
+ if( old_meta != new_meta )
36
+ Mattr.meta = [ self.class.name, self.id, meta_attr.to_s, get_metafied_attribute( meta_attr ) ]
37
+ end
38
+ end
39
+
40
+ def populate_metafied_attributes
41
+ define_accessors_for_attributes
42
+
43
+ attributes = Mattr.metas(self.class.name, self.id)
44
+ attributes.each do | meta_attr |
45
+ set_metafied_attribute( meta_attr.meta_key.to_sym, meta_attr.meta_value.to_s )
46
+ end
47
+ end
48
+
49
+ def singleton_class
50
+ class << self
51
+ self
52
+ end
53
+ end
54
+
55
+ def define_accessor_for_attribute(att)
56
+ singleton_class.send(:attr_accessor, att.to_sym)
57
+ end
58
+
59
+ def define_accessors_for_attributes
60
+ self.metafied_attrs.each do | meta_attr |
61
+ unless respond_to?(meta_attr.to_s)
62
+ define_accessor_for_attribute(meta_attr)
63
+ end
64
+ end
65
+ end
66
+
67
+ def self.included object
68
+ super
69
+ object.after_initialize :populate_metafied_attributes
70
+ object.after_find :populate_metafied_attributes
71
+ object.after_save :write_metafied_attributes
72
+ end
73
+
74
+ def set_metafied_attribute(att, value = nil)
75
+ unless respond_to?(att.to_s + '=')
76
+ define_accessor_for_attribute(att)
77
+ end
78
+
79
+ send(att.to_s + '=', value)
80
+ end
81
+
82
+ def get_metafied_attribute(att)
83
+ unless respond_to?(att.to_s)
84
+ define_accessor_for_attribute(att)
85
+ end
86
+
87
+ send(att.to_s)
88
+ end
89
+
90
+ # overrides update_attributes to take meta attributes into account
91
+ def update_attributes(attributes)
92
+ attributes.each do |n,v|
93
+ if has_metafied_attribute?(n.to_sym)
94
+ set_metafied_attribute( n.to_sym, v )
95
+ write_metafied_attribute( n.to_sym )
96
+ end
97
+ end
98
+
99
+ attributes.nil? ? super(nil) : super(attributes.except(*self.metafied_attrs))
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,9 @@
1
+ require 'rails'
2
+ require 'active_record/base'
3
+ require 'metafy'
4
+
5
+ module Metafy
6
+ class Engine < Rails::Engine
7
+ config.autoload_paths += %W(#{config.root})
8
+ end
9
+ end
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "metafy"
6
+ s.version = "1.0.1"
7
+ s.platform = Gem::Platform::RUBY
8
+ s.authors = ["supercleanse","btoone"]
9
+ s.email = ["support@caseproof.com"]
10
+ s.homepage = "http://blairwilliams.com/rails/metafy"
11
+ s.summary = %q{Dynamic Attributes Engine for Rails 3}
12
+ s.description = %q{Metafy is a Gem for Rails 3 that makes it possible to easily add dynamic attributes to any ActiveRecord model. These lightweight, meta attributes work just like normal database column attributes on your ActiveRecord model and are fully searchable (i.e. you can find records by values contained in these dynamic attributes).}
13
+
14
+ s.add_dependency('rails','>= 3.0.3')
15
+ s.license = 'MIT'
16
+
17
+ s.rubyforge_project = "metafy"
18
+
19
+ s.files = `git ls-files`.split("\n")
20
+ #s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
21
+ #s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
22
+ #s.require_paths = ["app","config","lib"]
23
+ end
@@ -0,0 +1,13 @@
1
+ ENV["RAILS_ENV"] = "test"
2
+ require File.expand_path('../../config/environment', __FILE__)
3
+ require 'rails/test_help'
4
+
5
+ class ActiveSupport::TestCase
6
+ # Setup all fixtures in test/fixtures/*.(yml|csv) for all tests in alphabetical order.
7
+ #
8
+ # Note: You'll currently still have to declare fixtures explicitly in integration tests
9
+ # -- they do not yet inherit this setting
10
+ fixtures :all
11
+
12
+ # Add more helper methods to be used by all tests here...
13
+ end
@@ -0,0 +1,7 @@
1
+ require 'test_helper'
2
+
3
+ class MattrTest < ActiveSupport::TestCase
4
+ test "test meta attributes" do
5
+ assert true
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: metafy
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 1.0.1
6
+ platform: ruby
7
+ authors:
8
+ - supercleanse
9
+ - btoone
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+
14
+ date: 2011-05-10 00:00:00 Z
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: rails
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: 3.0.3
25
+ type: :runtime
26
+ version_requirements: *id001
27
+ description: Metafy is a Gem for Rails 3 that makes it possible to easily add dynamic attributes to any ActiveRecord model. These lightweight, meta attributes work just like normal database column attributes on your ActiveRecord model and are fully searchable (i.e. you can find records by values contained in these dynamic attributes).
28
+ email:
29
+ - support@caseproof.com
30
+ executables: []
31
+
32
+ extensions: []
33
+
34
+ extra_rdoc_files: []
35
+
36
+ files:
37
+ - .gitignore
38
+ - Gemfile
39
+ - MIT-LICENSE
40
+ - README.rdoc
41
+ - Rakefile
42
+ - app/models/mattr.rb
43
+ - lib/generators/metafy_generator.rb
44
+ - lib/generators/templates/create_mattrs.rb
45
+ - lib/metafy.rb
46
+ - lib/metafy/activerecord_methods.rb
47
+ - lib/metafy/base.rb
48
+ - lib/metafy/engine.rb
49
+ - metafy.gemspec
50
+ - test/test_helper.rb
51
+ - test/unit/mattr_test.rb
52
+ homepage: http://blairwilliams.com/rails/metafy
53
+ licenses:
54
+ - MIT
55
+ post_install_message:
56
+ rdoc_options: []
57
+
58
+ require_paths:
59
+ - lib
60
+ required_ruby_version: !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: "0"
66
+ required_rubygems_version: !ruby/object:Gem::Requirement
67
+ none: false
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: "0"
72
+ requirements: []
73
+
74
+ rubyforge_project: metafy
75
+ rubygems_version: 1.7.2
76
+ signing_key:
77
+ specification_version: 3
78
+ summary: Dynamic Attributes Engine for Rails 3
79
+ test_files: []
80
+