sencha-model 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
6
+ *.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in shopify-api-limits.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,35 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ sencha-model (0.5.0)
5
+
6
+ GEM
7
+ remote: http://rubygems.org/
8
+ specs:
9
+ activemodel (3.0.7)
10
+ activesupport (= 3.0.7)
11
+ builder (~> 2.1.2)
12
+ i18n (~> 0.5.0)
13
+ activerecord (3.0.7)
14
+ activemodel (= 3.0.7)
15
+ activesupport (= 3.0.7)
16
+ arel (~> 2.0.2)
17
+ tzinfo (~> 0.3.23)
18
+ activesupport (3.0.7)
19
+ arel (2.0.10)
20
+ builder (2.1.2)
21
+ extlib (0.9.15)
22
+ i18n (0.5.0)
23
+ mocha (0.9.12)
24
+ shoulda (2.11.3)
25
+ tzinfo (0.3.27)
26
+
27
+ PLATFORMS
28
+ ruby
29
+
30
+ DEPENDENCIES
31
+ activerecord (>= 3.0.0)
32
+ extlib
33
+ mocha
34
+ sencha-model!
35
+ shoulda
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Chris Scott
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.
data/README.rdoc ADDED
@@ -0,0 +1,113 @@
1
+ = Sencha::Model
2
+
3
+ A simple Model mixin with adapters for various ORM frameworks such as ActiveRecord, DataMapper and MongoMapper. Sencha::Model was originally created as part of the gem extjs-mvc[http://github.com/extjs/mvc] to assist with auto-generating ExtJS Stores (Ext.data.Store). However, it can be useful for a variety of Javascript frameworks for rendering data on the client.
4
+
5
+
6
+ === Installation
7
+ % sudo gem install gemcutter
8
+ % sudo gem install sencha-model
9
+
10
+
11
+ === An ORM Model mixin: Sencha::Model
12
+ sencha-model contains Model mixin named <tt>Sencha::Model</tt> which works for <b>three</b> popular ORM frameworks, ActiveRecord, DataMapper and MongoMapper. The API for each framework is identical and an adapter can be created for just about any
13
+ ORM in about an hour.
14
+
15
+ Simply include the mixin into your model. Use the class-method <tt>sencha_fields</tt> to specify those
16
+ fields with will be used to render a record to Hash for later JSON-encoding.
17
+
18
+ class User < ActiveRecord::Base
19
+ include Sencha::Model
20
+
21
+ sencha_fields :exclude => [:password, :password_confirmation]
22
+
23
+ # OR
24
+ sencha_fields :name, :description
25
+
26
+ # OR
27
+ sencha_fields :only => [:name, :description] # actually the same as above
28
+
29
+ # OR
30
+ sencha_fields :additional => [:computed] # includes all database columns and an additional computed field
31
+
32
+ # OR define a column as a Hash
33
+ sencha_fields :description, :name => {"sortDir" => "ASC"}, :created_at => {"dateFormat" => "c"}
34
+
35
+ # OR render associations, association-fields will have their "mapping" property set automatically
36
+ sencha_fields :name, :description, :company => [:name, :description]
37
+
38
+ def computed
39
+ name.blank? ? login : name
40
+ end
41
+ end
42
+
43
+ After including the model mixin <tt>Sencha::Model</tt>, try typing the following in <tt>irb</tt> console:
44
+ >> User.sencha_schema
45
+ => { :idProperty=>"id", :fields=>[
46
+ {:type=>'int', :allowBlank=>true, :name=>"id"},
47
+ {:type=>'string', :allowBlank=>false, :name=>"first", :defaultValue => nil},
48
+ {:type=>'string', :allowBlank=>false, :name=>"last", :defaultValue => nil},
49
+ {:type=>'string', :allowBlank=>false, :name=>"email", :defaultValue => nil}
50
+ ]}
51
+
52
+ An auto-generated schema. This field-names were originally designed to be consumed by an <tt>Ext.data.Store</tt> from the {Ext JS Framework}[http://extjs.com]. TODO: make the field-names configurable.
53
+
54
+ You can also define different sets of fields for different representations of your model.
55
+
56
+ E.g. with the following definition:
57
+
58
+ class User < ActiveRecord::Base
59
+ include ExtJS::Model
60
+
61
+ sencha_fieldset :grid, [
62
+ :name,
63
+ :description,
64
+ {:company => [:name, :description]}
65
+ ]
66
+
67
+ sencha_fieldset :combo, [:full_name]
68
+
69
+ ##
70
+ # computed field
71
+ #
72
+ def full_name
73
+ "#{first_name} #{name}"
74
+ end
75
+ end
76
+
77
+ You can get store configs for both representations with
78
+ User.sencha_schema(:grid)
79
+ or
80
+ User.sencha_schema(:combo)
81
+
82
+ And the corresponding data for the representations with
83
+ User.first.to_record(:grid)
84
+ or
85
+ User.first.to_record(:combo)
86
+
87
+ === A Testing Mixin: Sencha::TestMacros
88
+ The <tt>sencha</tt> Gem includes a small set of testing macros to help unit-test models.
89
+ Using this macro requires the 'Shoulda' gem from thoughtbot
90
+
91
+ ==== Usage
92
+ In individual model unit tests:
93
+ class ModelTest < ActiveSupport::TestCase
94
+ should_have_sencha_fields_for_fieldset :fieldset_name, [:name, :email, :city]
95
+ #...
96
+ #other tests
97
+ end
98
+
99
+
100
+ == Note on Patches/Pull Requests
101
+
102
+ * Fork the project.
103
+ * Make your feature addition or bug fix.
104
+ * Add tests for it. This is important so I don't break it in a
105
+ future version unintentionally.
106
+ * Commit, do not mess with rakefile, version, or history.
107
+ (if you want to have your own version, that is fine but
108
+ bump version in a commit by itself I can ignore when I pull)
109
+ * Send me a pull request. Bonus points for topic branches.
110
+
111
+ == Copyright
112
+
113
+ Copyright (c) 2009 Chris Scott. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,26 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+
5
+ require 'rake/testtask'
6
+ Rake::TestTask.new(:test) do |test|
7
+ test.libs << 'lib' << 'test'
8
+ test.pattern = 'test/**/*_test.rb'
9
+ test.verbose = true
10
+ end
11
+
12
+ begin
13
+ require 'rcov/rcovtask'
14
+ Rcov::RcovTask.new do |test|
15
+ test.libs << 'test'
16
+ test.pattern = 'test/**/*_test.rb'
17
+ test.verbose = true
18
+ end
19
+ rescue LoadError
20
+ task :rcov do
21
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
22
+ end
23
+ end
24
+
25
+
26
+ task :default => :test
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.4.0
@@ -0,0 +1,17 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+
3
+ module Sencha
4
+ module Model
5
+ require 'sencha-model/model'
6
+
7
+ # Detect orm, include appropriate mixin.
8
+ if defined?(ActiveRecord)
9
+ require 'sencha-model/adapters/active_record'
10
+ elsif defined?(DataMapper)
11
+ require 'sencha-model/adapters/data_mapper'
12
+ elsif defined?(MongoMapper)
13
+ require 'sencha-model/adapters/mongo_mapper'
14
+ end
15
+ end
16
+ end
17
+
@@ -0,0 +1,88 @@
1
+ ##
2
+ # ActiveRecord adapter to Whorm::Model mixin.
3
+ #
4
+ module Sencha
5
+ module Model
6
+ module ClassMethods
7
+ def sencha_primary_key
8
+ self.primary_key.to_sym
9
+ end
10
+
11
+ def sencha_column_names
12
+ self.column_names.map(&:to_sym)
13
+ end
14
+
15
+ def sencha_columns_hash
16
+ self.columns_hash.symbolize_keys
17
+ end
18
+
19
+ ##
20
+ # determine if supplied Column object is nullable
21
+ # @param {ActiveRecord::ConnectionAdapters::Column}
22
+ # @return {Boolean}
23
+ #
24
+ def sencha_allow_blank(col)
25
+ # if the column is the primary key always allow it to be blank.
26
+ # Otherwise we could not create new records with sencha because
27
+ # new records have no id and thus cannot be valid
28
+ col.name == self.primary_key || col.null
29
+ end
30
+
31
+ ##
32
+ # returns the default value
33
+ # @param {ActiveRecord::ConnectionAdapters::Column}
34
+ # @return {Mixed}
35
+ #
36
+ def sencha_default(col)
37
+ col.default
38
+ end
39
+
40
+ ##
41
+ # returns the corresponding column name of the type column for a polymorphic association
42
+ # @param {String/Symbol} the id column name for this association
43
+ # @return {Symbol}
44
+ def sencha_polymorphic_type(id_column_name)
45
+ id_column_name.to_s.gsub(/_id\Z/, '_type').to_sym
46
+ end
47
+
48
+ ##
49
+ # determine datatype of supplied Column object
50
+ # @param {ActiveRecord::ConnectionAdapters::Column}
51
+ # @return {String}
52
+ #
53
+ def sencha_type(col)
54
+ type = col.type.to_s
55
+ case type
56
+ when "datetime", "date", "time", "timestamp"
57
+ type = "date"
58
+ when "text"
59
+ type = "string"
60
+ when "integer"
61
+ type = "int"
62
+ when "decimal"
63
+ type = "float"
64
+ end
65
+ type
66
+ end
67
+
68
+ ##
69
+ # return a simple, normalized list of AR associations having the :name, :type and association class
70
+ # @return {Array}
71
+ #
72
+ def sencha_associations
73
+ @sencha_associations ||= self.reflections.inject({}) do |memo, (key, assn)|
74
+ type = (assn.macro === :has_many || assn.macro === :has_and_belongs_to_many) ? :many : assn.macro
75
+ memo[key.to_sym] = {
76
+ :name => key.to_sym,
77
+ :type => type,
78
+ :class => assn.options[:polymorphic] ? nil : assn.class_name.constantize,
79
+ :foreign_key => assn.association_foreign_key.to_sym,
80
+ :is_polymorphic => !!assn.options[:polymorphic]
81
+ }
82
+ memo
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+
@@ -0,0 +1,66 @@
1
+ ##
2
+ # DataMapper adapter for Whorm::Model mixin
3
+ #
4
+
5
+ module Sencha
6
+ module Model
7
+ module ClassMethods
8
+
9
+ def sencha_primary_key
10
+ self.key.first.name
11
+ end
12
+
13
+ def sencha_column_names
14
+ self.properties.collect {|p| p.name.to_s }
15
+ end
16
+
17
+ def sencha_columns_hash
18
+ if @sencha_columns_hash.nil?
19
+ @sencha_columns_hash = {}
20
+ self.properties.each do |p|
21
+ @sencha_columns_hash[p.name] = p
22
+ end
23
+ end
24
+ @sencha_columns_hash
25
+ end
26
+
27
+ def sencha_allow_blank(col)
28
+ (col === self.key.first) ? true : col.nullable?
29
+ end
30
+
31
+ def sencha_type(col)
32
+ type = ((col.type.respond_to?(:primitive)) ? col.type.primitive : col.type).to_s
33
+ case type
34
+ when "DateTime", "Date", "Time"
35
+ type = :date
36
+ when "String"
37
+ type = :string
38
+ when "Float"
39
+ type = :float
40
+ when "Integer", "BigDecimal"
41
+ type = :int
42
+ else
43
+ type = "auto"
44
+ end
45
+ end
46
+
47
+ def sencha_associations
48
+ if @sencha_associations.nil?
49
+ @sencha_associations = {}
50
+ self.relationships.keys.each do |key|
51
+ assn = self.relationships[key]
52
+ @sencha_associations[key.to_sym] = {
53
+ :name => key,
54
+ :type => type = (assn.options[:max].nil? && assn.options[:min].nil?) ? :belongs_to : (assn.options[:max] > 1) ? :many : nil ,
55
+ :class => assn.parent_model,
56
+ :foreign_key => assn.child_key.first.name,
57
+ :is_polymorphic => false # <-- No impl. for DM is_polymorphic. Anyone care to implement this?
58
+ }
59
+ end
60
+ end
61
+ @sencha_associations
62
+ end
63
+ end
64
+ end
65
+ end
66
+
@@ -0,0 +1,64 @@
1
+ ##
2
+ # MongoMapper adapter to Sencha::Model mixin
3
+ #
4
+
5
+ module Sencha
6
+ module Model
7
+ ##
8
+ # ClassMethods
9
+ #
10
+ module ClassMethods
11
+
12
+ def sencha_primary_key
13
+ :id
14
+ end
15
+
16
+ def sencha_column_names
17
+ self.column_names
18
+ end
19
+
20
+ def sencha_columns_hash
21
+ self.keys
22
+ end
23
+
24
+ def sencha_associations
25
+ @sencha_associations ||= self.associations.inject({}) do |memo, (key, assn)|
26
+ memo[key.to_sym] = {
27
+ :name => key.to_sym,
28
+ :type => assn.type,
29
+ :class => assn.class_name.constantize,
30
+ :foreign_key => assn.foreign_key,
31
+ :is_polymorphic => false
32
+ }
33
+ memo
34
+ end
35
+ end
36
+
37
+ def sencha_type(col)
38
+ type = col.type.to_s
39
+ case type
40
+ when "DateTime", "Date", "Time"
41
+ type = :date
42
+ when "String"
43
+ type = :string
44
+ when "Float"
45
+ type = :float
46
+ when "Integer", "BigDecimal"
47
+ type = :int
48
+ else
49
+ type = "auto"
50
+ end
51
+ end
52
+
53
+ def sencha_allow_blank(col)
54
+ (col.name == '_id') || (col.options[:required] != true)
55
+ end
56
+
57
+ def sencha_default(col)
58
+ col.default_value
59
+ end
60
+
61
+ end
62
+ end
63
+ end
64
+