fm_store 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,46 @@
1
+ # encoding: utf-8
2
+ module FmStore
3
+ module Criterion
4
+ module Optional
5
+ # Tell FileMaker how many records in the found set to skip.
6
+ # Use together with +limit+ to page through the records
7
+ def skip(value = 20)
8
+ @options[:skip_records] = value
9
+ self
10
+ end
11
+
12
+ def limit(value = 20)
13
+ @options[:max_records] = value
14
+ self
15
+ end
16
+
17
+ # +ascend+ or +descend+
18
+ def order(field_and_orders)
19
+ sorts = field_and_orders.split(",").map(&:strip)
20
+ sort_field = []
21
+ sort_order = []
22
+
23
+ sorts.each do |s|
24
+ field, order = s.split(" ")
25
+ order = "asc" unless order
26
+
27
+ fm_name = klass.find_fm_name(field)
28
+
29
+ order = "ascend" if order.downcase == "asc"
30
+ order = "descend" if order.downcase == "desc"
31
+
32
+ sort_field << fm_name
33
+ sort_order << order
34
+ end
35
+
36
+ @options[:sort_field] = sort_field
37
+ @options[:sort_order] = sort_order
38
+
39
+ self
40
+ end
41
+
42
+ # logical_operator
43
+ # modification_id
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,20 @@
1
+ module Rfm
2
+ module Metadata
3
+ class Field
4
+ def coerce(value, resultset)
5
+ return nil if (value.nil? || value.empty?)
6
+ case self.result
7
+ when "text" then value
8
+ when "number" then BigDecimal.new(value)
9
+ when "date" then Date.strptime(value, resultset.date_format)
10
+ when "time" then DateTime.strptime("1/1/-4712 #{value}", "%m/%d/%Y #{resultset.time_format}")
11
+ when "timestamp" then DateTime.strptime(value, resultset.timestamp_format)
12
+ when "container" then URI.parse("#{resultset.server.scheme}://#{resultset.server.host_name}:#{resultset.server.port}#{value}")
13
+ else nil
14
+ end
15
+ rescue
16
+ nil
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,14 @@
1
+ # encoding: utf-8
2
+ module FmStore
3
+ class Field
4
+ attr_reader :name, :type, :fm_name, :searchable, :identity
5
+
6
+ def initialize(name, type, options = {})
7
+ @name = name
8
+ @fm_name = options[:fm_name] || name
9
+ @type = type
10
+ @searchable = options[:searchable] || false
11
+ @identity = options[:identity] || false
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,36 @@
1
+ # encoding: utf-8
2
+ module FmStore
3
+ module Fields
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ class_inheritable_accessor :fields
8
+
9
+ self.fields = {}
10
+ end
11
+
12
+ module ClassMethods
13
+ # Defines all the fields that are available from the layout.
14
+ def field(name, type, options = {})
15
+ set_field(name.to_s, type, options)
16
+ end
17
+
18
+ protected
19
+
20
+ def set_field(name, type, options)
21
+ # We key FileMaker name rather then user specified name
22
+ fields[options[:fm_name] || name] = Field.new(name, type, options)
23
+ create_accessors(name)
24
+ end
25
+
26
+ def create_accessors(name)
27
+ define_method(name) { instance_variable_get("@#{name}") }
28
+ define_method("#{name}=") { |value| instance_variable_set("@#{name}", value) }
29
+ define_method("#{name}?") do
30
+ attr = instance_variable_get("@#{name}")
31
+ (@type == Boolean) ? attr == true : attr.present?
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,36 @@
1
+ # encoding: utf-8
2
+ module FmStore
3
+ module Finders
4
+
5
+ # Criteria
6
+ [:where, :limit, :skip, :order, :exclude, :search, :id, :fm_id].each do |name|
7
+ define_method(name) do |*args|
8
+ criteria.send(name, *args)
9
+ end
10
+ end
11
+
12
+ [:in, :custom_query].each do |name|
13
+ define_method(name) do |*args|
14
+ criteria_query.send(name, *args)
15
+ end
16
+ end
17
+
18
+ def criteria
19
+ Criteria.new(self)
20
+ end
21
+
22
+ def criteria_query
23
+ Criteria.new(self, true)
24
+ end
25
+
26
+ # Will always return an array properly cast to the correct type
27
+ # def find(hash_or_record_id, options = {})
28
+ # where(hash_or_record_id, options)
29
+ # end
30
+
31
+ def total
32
+ criteria.paginate(:per_page => 1).total_entries
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,156 @@
1
+ # encoding: utf-8
2
+ module FmStore
3
+ module Layout
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ include FmStore::Components
8
+ self.include_root_in_json = false
9
+
10
+ cattr_accessor :layout, :database
11
+
12
+ attr_reader :new_record, :record_id, :mod_id
13
+
14
+ define_model_callbacks :create, :save, :update, :validation, :destroy
15
+ end
16
+
17
+ module ClassMethods
18
+ def set_layout(layout)
19
+ self.layout = layout
20
+ end
21
+
22
+ def set_database(database)
23
+ self.database = database
24
+ end
25
+
26
+ # Calling self.fields will ideally match here
27
+ # See FieldControl
28
+ def fm_fields
29
+ conn = Connection.establish_connection(self)
30
+ rs = conn.any.first.keys.inspect
31
+ end
32
+
33
+ # Return the real FileMaker, nil otherwise
34
+ def find_fm_name(attribute_name)
35
+ if fields.has_key?(attribute_name)
36
+ return attribute_name
37
+ else
38
+ f = fields.find { |a| a.last.name == attribute_name }
39
+
40
+ f.last.fm_name if f
41
+ end
42
+ end
43
+
44
+ def find_fm_type(attribute_name)
45
+ f = fields.find { |a| a.last.name == attribute_name }
46
+
47
+ f.last.type if f
48
+ end
49
+
50
+ def searchable_fields
51
+ fields.map(&:last).select(&:searchable).map(&:name)
52
+ end
53
+
54
+ def identity
55
+ fields.map(&:last).find(&:identity).try(:fm_name) || "-recid"
56
+ end
57
+
58
+ # Drop-down, for example
59
+ # http://host/fmi/xml/FMPXMLLAYOUT.xml?-db=jobs+&-lay=jobs&-view=
60
+ def value_lists
61
+ conn = Connection.establish_connection(self)
62
+ conn.value_lists
63
+ end
64
+
65
+ def first
66
+ limit(1).first
67
+ end
68
+ end
69
+
70
+ def initialize(attributes = {})
71
+ @associations = {}
72
+ @new_record = true
73
+ process(attributes)
74
+ end
75
+
76
+ def fm_attributes
77
+ attrs = {}
78
+
79
+ fields.each do |fm_attr, field|
80
+ ivar = send("#{field.name}")
81
+
82
+ type = field.type
83
+
84
+ if type == Date
85
+ ivar = ivar.strftime("%m/%d/%Y") if ivar
86
+ elsif type == DateTime
87
+ ivar = ivar.strftime("%m/%d/%Y %H:%M:%S") if ivar
88
+ elsif type == Time
89
+ ivar = ivar.strftime("%H:%M") if ivar
90
+ end
91
+
92
+ # case ivar
93
+ # when Date
94
+ # ivar = ivar.strftime("%m/%d/%Y")
95
+ # when DateTime
96
+ # ivar = ivar.strftime("%m/%d/%Y %H:%M:%S")
97
+ # when Time
98
+ # ivar = ivar.strftime("%H:%M")
99
+ # end
100
+
101
+ attrs[fm_attr] = ivar if ivar # ignore nil attributes
102
+ end
103
+
104
+ attrs
105
+ end
106
+
107
+ def attributes
108
+ @attributes = {}
109
+
110
+ fields.each do |fm_attr, field|
111
+ ivar = send("#{field.name}")
112
+ @attributes[field.name] = ivar
113
+ end
114
+
115
+ return @attributes
116
+ end
117
+
118
+ def reload
119
+ @associations = {}
120
+ self.class.id(id)
121
+ end
122
+
123
+ def id
124
+ self.class.identity == "-recid" ? @record_id : send(self.class.fields[self.class.identity].name)
125
+ end
126
+
127
+ def new_record?
128
+ @new_record
129
+ end
130
+
131
+ def to_param
132
+ id.to_s if id
133
+ end
134
+
135
+ # Require by ActiveModel
136
+ def to_model
137
+ self
138
+ end
139
+
140
+ def to_key
141
+ id if id
142
+ end
143
+
144
+ def persisted?
145
+ !new_record?
146
+ end
147
+
148
+ protected
149
+
150
+ def process(attributes)
151
+ attributes.each do |k, v|
152
+ send("#{k}=", v) if respond_to?(k)
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,27 @@
1
+ # encoding: utf-8
2
+ module FmStore
3
+ module Paging
4
+ def paginate(opts = {})
5
+ options[:max_records] = opts[:per_page] || 30
6
+
7
+ if opts[:page]
8
+ options[:skip_records] = (opts[:page].to_i - 1) * options[:max_records].to_i
9
+ end
10
+
11
+ collection = execute(true)
12
+
13
+ WillPaginate::Collection.create(page, per_page, count) do |pager|
14
+ pager.replace(collection)
15
+ end
16
+ end
17
+
18
+ def page
19
+ skips, limits = options[:skip_records], options[:max_records]
20
+ (skips && limits) ? (skips + limits) / limits : 1
21
+ end
22
+
23
+ def per_page
24
+ (options[:max_records] || 30).to_i
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,109 @@
1
+ # encoding: utf-8
2
+ module FmStore
3
+ module Persistence
4
+ extend ActiveSupport::Concern
5
+
6
+ # The place where all the persistence took place, like insert, update
7
+ module ClassMethods
8
+ def create(attributes = {})
9
+
10
+ end
11
+ end
12
+
13
+ # Instance methods
14
+ def save
15
+ create_or_update
16
+ end
17
+
18
+ def update_attributes(attributes = {})
19
+ if valid?
20
+ attrs = {}
21
+
22
+ attributes.each do |field, value|
23
+ field = field.to_s
24
+
25
+ fm_name = self.class.find_fm_name(field)
26
+ type = self.class.find_fm_type(field)
27
+
28
+ if fm_name
29
+ if type == Date
30
+ if value.blank?
31
+ value = '' # clear the date
32
+ else
33
+ value = value.strftime("%m/%d/%Y")
34
+ end
35
+ elsif type == DateTime
36
+ if value.blank?
37
+ value = ''
38
+ else
39
+ value = value.strftime("%m/%d/%Y %H:%M:%S")
40
+ end
41
+ elsif type == Time
42
+ if value.blank?
43
+ value = ''
44
+ else
45
+ value = value.strftime("%H:%M")
46
+ end
47
+ end
48
+
49
+ attrs[fm_name] = value
50
+ end
51
+ end
52
+
53
+ run_callbacks(:save) do
54
+ conn = Connection.establish_connection(self.class)
55
+ result = conn.edit(@record_id, attrs)
56
+
57
+ return FmStore::Builders::Single.build(result, self.class)
58
+ end;self # just in case
59
+ else
60
+ false
61
+ end
62
+ end
63
+
64
+ # Throws Rfm::Error::RecordAccessDeniedError if no permission to delete
65
+ def destroy
66
+ run_callbacks(:destroy) do
67
+ unless @record_id.nil?
68
+ conn = Connection.establish_connection(self.class)
69
+ conn.delete(@record_id)
70
+ end
71
+ end
72
+ end
73
+
74
+ alias :delete :destroy
75
+
76
+ protected
77
+
78
+ # Will always return +self+
79
+ def create_or_update
80
+ result = new_record? ? create : update
81
+ end
82
+
83
+ def create
84
+ if valid?
85
+ run_callbacks(:save) do
86
+ conn = Connection.establish_connection(self.class)
87
+ result = conn.create(self.fm_attributes)
88
+
89
+ @record_id = result[0].record_id
90
+ @new_record = false
91
+ end; self
92
+ else
93
+ false
94
+ end
95
+ end
96
+
97
+ def update
98
+ if valid?
99
+ run_callbacks(:save) do
100
+ conn = Connection.establish_connection(self.class)
101
+ result = conn.edit(@record_id, self.fm_attributes)
102
+ end; self
103
+ else
104
+ false
105
+ end
106
+ end
107
+
108
+ end
109
+ end