arusarka-dynamic-active-resource 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ begin
2
+ require 'active_resource'
3
+ rescue LoadError
4
+ require 'rubygems'
5
+ require 'active_resource'
6
+ end
7
+
8
+ $:.unshift File.expand_path(File.dirname(__FILE__))
9
+
10
+ require 'dynamic_active_resource/associations'
11
+ require 'dynamic_active_resource/common_dynamic_class_instance_methods'
12
+ require 'dynamic_active_resource/common_class_methods'
13
+ require 'dynamic_active_resource/helpers'
14
+ require 'dynamic_active_resource/version'
15
+ require 'dynamic_active_resource/base'
16
+
17
+ module DynamicActiveResource
18
+ end
19
+
20
+ # shorter name
21
+ DynAR = DynamicActiveResource
@@ -0,0 +1,21 @@
1
+ require 'dynamic_active_resource/associations/base'
2
+ require 'dynamic_active_resource/associations/has_many'
3
+ require 'dynamic_active_resource/associations/belongs_to'
4
+
5
+ module DynamicActiveResource
6
+ module Associations
7
+ def has_many(assosiation_sym, options = {})
8
+ h_m_association = HasMany.new(assosiation_sym, options)
9
+ associations = self.instance_variable_get(:@associations) || []
10
+ associations << h_m_association
11
+ self.instance_variable_set(:@associations, associations)
12
+ end
13
+
14
+ def belongs_to(association_sym, options = {})
15
+ b_t_association = BelongsTo.new(association_sym, options)
16
+ associations = self.instance_variable_get(:@associations) || []
17
+ associations << b_t_association
18
+ self.instance_variable_set(:@associations, associations)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,23 @@
1
+ module DynamicActiveResource
2
+ module Associations
3
+ class Base
4
+ attr_reader :method_name
5
+ def initialize(method_name, options = {})
6
+ @method_name = method_name.to_s
7
+ class_name = options[:through] || @method_name.singularize.camelize
8
+ @associated_class_name = class_name
9
+ end
10
+
11
+ # fetches the resources for the association. requires the caller to be
12
+ # passed through self.
13
+ def resources_for(caller)
14
+ raise 'Not Implemented'
15
+ end
16
+
17
+ private
18
+ def associated_class
19
+ eval(@associated_class_name)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,11 @@
1
+ module DynamicActiveResource
2
+ module Associations
3
+ class BelongsTo < Associations::Base
4
+ def resources_for(caller)
5
+ site_elements = caller.class.site.path.split('/')
6
+ associated_resource_id = site_elements[-1]
7
+ associated_class.find(associated_resource_id)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,17 @@
1
+ module DynamicActiveResource
2
+ module Associations
3
+ class HasMany < Base
4
+ def set_associated_class_attributes(caller)
5
+ associated_class_site = File.join(caller.class.site.to_s, caller.class.collection_name, caller.resource_identifier)
6
+ associated_class.site = associated_class_site
7
+ associated_class.user = caller.class.user
8
+ associated_class.password = caller.class.password
9
+ end
10
+
11
+ def resources_for(caller)
12
+ set_associated_class_attributes(caller)
13
+ associated_class.find(:all)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,210 @@
1
+ module DynamicActiveResource
2
+ # ActiveResource makes connecting to rest resources very easy. However it has one problem
3
+ # and a big one at that. If you try setting the authentication credentials or the site or
4
+ # collection name, element name for the class for the second time it doesn't work. E.g.
5
+ #
6
+ # class Person < ActiveResource::Base
7
+ # self.site = 'http://localhost:9090/'
8
+ # end
9
+ #
10
+ # After sometime you change it to
11
+ #
12
+ # Person.site = 'https://org-server/my_proj/'
13
+ # Person.user = 'admin'
14
+ # Person.password = 'secret'
15
+ #
16
+ # Then you do
17
+ #
18
+ # Person.find(:all) => It bombs
19
+ #
20
+ # This class provides a mechanism by which you can get rid of this problem. Extend DynamicActiveResource::Base
21
+ # class in the actual class itself. Do not extend the extended class from ActiveResource::Base.
22
+ #
23
+ # E.g.
24
+ #
25
+ # class Person < DynamicActiveResource::Base
26
+ # end
27
+ #
28
+ # set the credentials
29
+ #
30
+ # Person.site = 'http://localhost:8080'
31
+ # Person.user = 'foo'
32
+ # Person.password = 'bar'
33
+ #
34
+ # Thats it. Now create some objects
35
+ #
36
+ # asur = Person.new(:name => 'Asur', :job => 'fooling around', :status => 'Single and ready 2 mingle')
37
+ # asur.save
38
+ #
39
+ # Now change the class attributes
40
+ #
41
+ # Person.site = 'https://org-server/mingle'
42
+ # Person.collection_name = 'boring_people'
43
+ #
44
+ # Now instantiate an object
45
+ #
46
+ # rakhshas = Person.new(:name => 'Rakhshas', :job => 'eating people', :status => 'just woke up and hungry')
47
+ # rakhshas.save => Voila !!!!!!! it works
48
+ #
49
+ # CUSTOMIZATIONS
50
+ # --------------
51
+ #
52
+ # No amount of wrapping can provide very detailed customizations. Either you have a lot of methods
53
+ # that are not being used or there is hardly anything at all. To oversome this problem this module
54
+ # was written to provide only those methods which are common to most active resource objects.
55
+ # However if you want to have a little more control over your active resource objects its very easy.
56
+ # Here's how you would do it normally
57
+ #
58
+ # class Person < ActiveResource::Base
59
+ # def self.count
60
+ # find(:all).size
61
+ # end
62
+ #
63
+ # def occupation
64
+ # return job if job
65
+ # 'Unemployed'
66
+ # end
67
+ # end
68
+ #
69
+ # To do the same thing, here's how you do it using this library
70
+ #
71
+ # class Person < DynamicActiveResource::Base
72
+ # module DynamicClassSingletonMethods
73
+ # def count
74
+ # find(:all).size
75
+ # end
76
+ # end
77
+ #
78
+ # module DynamicClassInstanceMethods
79
+ # def occupation
80
+ # return job if job
81
+ # 'Unemployed'
82
+ # end
83
+ # end
84
+ # end
85
+ #
86
+ # The DynamicClassInstanceMethods will be available as instance methods in the objects created,
87
+ # DynamicClassSingletonMethods will be available as class methods in the class of the object. Also
88
+ # active resource associations are sometimes paginated. So even if you use find(:all), you get
89
+ # only the first few results. To overcome this, include in your DynamicClassSingletonMethods module
90
+ # a method called find_without_pagination. In that method route it to acitve resource find with
91
+ # proper parameters. An example of the method implementation is
92
+ #
93
+ # module DynamicClassSingletonMethods
94
+ # def find_without_pagination(*args)
95
+ # scope = args.slice!(0)
96
+ # options = args.slice!(0) || {}
97
+ # options[:params] ||= {}
98
+ # options[:params].merge!({:page => 'all'})
99
+ # # call ActiveResource::Base::find with proper options
100
+ # find(scope, options)
101
+ # end
102
+ # end
103
+ #
104
+ # The class level find method will automatically pick up this method if defined as a method in the
105
+ # DynamicClassSingeletonMethod module else it will pass it to find.
106
+ #
107
+ # ASSOCIATIONS
108
+ # ------------
109
+ #
110
+ # This gem also provides active record like associations(highly experimental). Right now it provides only two
111
+ # associations - 1) has_many, 2) belongs_to.
112
+ #
113
+ # 1) has_many
114
+ # -----------
115
+ #
116
+ # Taking the example from above the way it should be done
117
+ #
118
+ # class Person < DynamicActiveResource::Base
119
+ # has_many :cars
120
+ #
121
+ # module DynamicClassSingletonMethods
122
+ # def count
123
+ # find(:all).size
124
+ # end
125
+ # end
126
+ #
127
+ # module DynamicClassInstanceMethods
128
+ # def occupation
129
+ # return job if job
130
+ # 'Unemployed'
131
+ # end
132
+ #
133
+ # def resource_identifier
134
+ # name()
135
+ # end
136
+ # end
137
+ # end
138
+ #
139
+ # Next a car class has to be initialized
140
+ #
141
+ # class Car < DynARBase (alias for DynamicActiveResource::Base, inbuilt in the gem)
142
+ # end
143
+ #
144
+ # set resource options only at the top level( Person in this case)
145
+ #
146
+ # Person.site = 'http://localhost:8080/'
147
+ # Person.user = 'test'
148
+ # Person.password = 'secret'
149
+ #
150
+ # associations take care of setting the site in the children classes automatically
151
+ # you will notice that an additional method resource_identifier() has been defined
152
+ # in the parent class. It would be discussed shortly.
153
+ #
154
+ # The way associations work is if you do something like
155
+ #
156
+ # person = Person.find('asur').cars it will hit the url http://localhost:8080/people/asur/cars.xml
157
+ #
158
+ # You would notice that its getting the cars for the person with name 'asur'. It does so because
159
+ # in the Person class a method called resource_identifier has been defined which says that the id
160
+ # is actually name instead of the database id. If the id attribute in the xml is set appropriately
161
+ # (to 'name' in this case) then you do not need to define the method.
162
+ #
163
+ # 2) belongs_to
164
+ # -------------
165
+ #
166
+ # Again, referring to the example above
167
+ #
168
+ # class Person < DynamicActiveResource::Base
169
+ # has_many :cars
170
+ #
171
+ # module DynamicClassSingletonMethods
172
+ # def count
173
+ # find(:all).size
174
+ # end
175
+ # end
176
+ #
177
+ # module DynamicClassInstanceMethods
178
+ # def occupation
179
+ # return job if job
180
+ # 'Unemployed'
181
+ # end
182
+ #
183
+ # def resource_identifier
184
+ # name()
185
+ # end
186
+ # end
187
+ # end
188
+ #
189
+ # Next a car class has to be initialized
190
+ #
191
+ # class Car < DynARBase (alias for DynamicActiveResource::Base)
192
+ # belongs_to :person
193
+ # end
194
+ #
195
+ # Right now belongs_to supports only assocation with a single class. After defining this you
196
+ # automatically have a method 'person' available.
197
+ #
198
+ # car = Car.find('WB1234I')
199
+ # owner = car.person
200
+
201
+ class Base < ActiveResource::Base
202
+ def self.inherited(subclass)
203
+ subclass.extend(DynamicActiveResource::CommonClassMethods)
204
+ subclass.extend(DynamicActiveResource::Associations)
205
+ end
206
+ end
207
+ end
208
+
209
+ # shorter name
210
+ DynARBase = DynamicActiveResource::Base
@@ -0,0 +1,131 @@
1
+ module DynamicActiveResource
2
+ module CommonClassMethods
3
+ attr_reader :site, :user, :password
4
+
5
+ # creates an object of the class in which this module is extended
6
+ def new(args = {})
7
+ # @resource_class = create_resource_class() # should this be commented
8
+ @resource_class.new(args)
9
+ end
10
+
11
+ # sets the site for the class in which this module is extended
12
+ def site=(site)
13
+ if site != self.site
14
+ @site = site
15
+ uri = URI.parse(site)
16
+ @user = URI.decode(uri.user) if(uri.user)
17
+ @password = URI.decode(uri.password) if(uri.password)
18
+ @resource_class = self.send(:create_resource_class)
19
+ end
20
+ @site
21
+ end
22
+
23
+ # sets the user for the class in which this module is extended
24
+ def user=(user)
25
+ if user != self.user
26
+ @user = user
27
+ @resource_class = self.send(:create_resource_class) if(site)
28
+ end
29
+ @user
30
+ end
31
+
32
+ # sets the password for the class in which this module is extended
33
+ def password=(password)
34
+ if password != self.password
35
+ @password = password
36
+ @resource_class = self.send(:create_resource_class) if(site)
37
+ end
38
+ @password
39
+ end
40
+
41
+ # sets the collection name for the class in which this module is extended
42
+ def collection_name=(collection_name)
43
+ if collection_name != self.collection_name
44
+ @collection_name = collection_name
45
+ end
46
+ @collection_name
47
+ end
48
+
49
+ # sets the elment name for the class in which this module is extended
50
+ def element_name=(element_name)
51
+ if element_name != self.element_name
52
+ @element_name = element_name
53
+ end
54
+ @element_name
55
+ end
56
+
57
+ # collection name for the class in which this module is extended
58
+ def collection_name
59
+ @collection_name || class_name.underscore.pluralize
60
+ end
61
+
62
+ # element name for the class in which this module is extended
63
+ def element_name
64
+ @element_name || class_name.underscore
65
+ end
66
+
67
+ # routes to active resource find
68
+ def find(*args)
69
+ return @resource_class.find_without_pagination(*args) if(@resource_class.respond_to?(:find_without_pagination))
70
+ scope = args.slice!(0)
71
+ options = args.slice!(0) || {}
72
+ obj = @resource_class.find(scope, options)
73
+ obj
74
+ end
75
+
76
+ private
77
+ # creates an active resource class dynamically. All the attributes are set automatically. Avoid calling
78
+ # this method directly
79
+ def create_resource_class
80
+ # raise exceptions if any of site is not set
81
+ raise "Please set the site for #{self} class before using create_resource_class()." unless(self.site)
82
+
83
+ created_class = Class.new(ActiveResource::Base)
84
+
85
+ # set the resource options
86
+ created_class.site = self.site
87
+ created_class.user = self.user
88
+ created_class.password = self.password
89
+ created_class.collection_name = self.collection_name
90
+ created_class.element_name = self.element_name
91
+
92
+ created_class_name = "#{self}::#{class_name}#{Helpers.fast_token()}"
93
+ eval "#{created_class_name} = created_class"
94
+
95
+ # include the common dynamic methods
96
+ created_class.send(:include, DynamicActiveResource::CommonDynamicClassInstanceMethods)
97
+
98
+ # includes a module called InstanceMethods in the class created dynamically
99
+ # if it is defined inside the wrapper class
100
+ inst_meth_mod_name = instance_methods_module_name()
101
+ created_class.send(:include, self.const_get(inst_meth_mod_name.to_sym)) if inst_meth_mod_name
102
+
103
+ # extends the class created dynamically with a module called ClassMethods if
104
+ # it is defined inside the wrapper class
105
+ class_meth_mod_name = class_methods_module_name()
106
+ created_class.extend(self.const_get(class_meth_mod_name)) if class_meth_mod_name
107
+ # put the associations in the created class also
108
+ created_class.instance_variable_set(:@associations, @associations)
109
+
110
+ created_class
111
+ end
112
+
113
+ def class_name
114
+ self.name.split('::')[-1]
115
+ end
116
+
117
+ def instance_methods_module_name
118
+ inst_meth_mod_name = 'DynamicClassInstanceMethods'
119
+ self.constants.detect { |const| const.split('::')[-1] =~ /#{inst_meth_mod_name}/ }
120
+ end
121
+
122
+ def class_methods_module_name
123
+ class_meth_mod_name = 'DynamicClassSingletonMethods'
124
+ self.constants.detect { |const| const.split('::')[-1] =~ /#{class_meth_mod_name}/ }
125
+ end
126
+
127
+ def method_missing(meth_id, *args, &block)
128
+ @resource_class.send(meth_id, *args, &block)
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,38 @@
1
+ module DynamicActiveResource
2
+ module CommonDynamicClassInstanceMethods
3
+ def method_missing(method_symbol, *arguments) #:nodoc:
4
+ method_name = method_symbol.to_s
5
+ # dynamic setters and getters
6
+ case method_name.last
7
+ when "="
8
+ return attributes[method_name.first(-1)] = arguments.first
9
+ when "?"
10
+ return attributes[method_name.first(-1)]
11
+ else
12
+ # return attributes
13
+ return attributes[method_name] if(attributes.has_key?(method_name))
14
+ # return the association if it is a association method
15
+ return get_association(method_name) if(association_method?(method_name))
16
+ super
17
+ end
18
+ end
19
+
20
+ def resource_identifier
21
+ id()
22
+ end
23
+
24
+ private
25
+ def association_method?(method_name)
26
+ @associations ||= self.class.instance_variable_get(:@associations)
27
+ return false unless @associations
28
+ association = @associations.detect { |association| association.method_name.to_s == method_name.to_s }
29
+ association ? true : false
30
+ end
31
+
32
+ def get_association(method_name)
33
+ @associations ||= self.class.instance_variable_get(:@associations)
34
+ association = @associations.detect { |association| association.method_name.to_s == method_name.to_s }
35
+ association.resources_for(self)
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,33 @@
1
+ module DynamicActiveResource
2
+ module Helpers
3
+ def Helpers.fast_token
4
+ values = [
5
+ rand(0x0010000),
6
+ rand(0x0010000),
7
+ rand(0x0010000),
8
+ rand(0x0010000),
9
+ rand(0x0010000),
10
+ rand(0x1000000),
11
+ rand(0x1000000),
12
+ ]
13
+ "%04x%04x%04x%04x%04x%06x%06x" % values
14
+ end
15
+
16
+ def Helpers.encode2html(string)
17
+ html_char_map = {
18
+ '[' => '%5B', ']' => '%5D',
19
+ '(' => '%28', ')' => '%29',
20
+ ',' => '%2C', ' ' => '%20',
21
+ '=' => '%3D', '\'' => '%27',
22
+ '<' => '%3C', '>' => '%3E',
23
+ }
24
+
25
+ string.strip! if string
26
+ encoded_string = ''
27
+ string.each_char do |char|
28
+ encoded_string << (html_char_map[char] || char)
29
+ end
30
+ encoded_string
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,11 @@
1
+ module DynamicActiveResource
2
+ module Version
3
+ Major = '0'
4
+ Minor = '2'
5
+ Tiny = '1'
6
+
7
+ def self.to_s
8
+ [Major, Minor, Tiny].join('.')
9
+ end
10
+ end
11
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: arusarka-dynamic-active-resource
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - asur
@@ -31,6 +31,16 @@ extensions: []
31
31
  extra_rdoc_files:
32
32
  - README
33
33
  files:
34
+ - lib/dynamic_active_resource/associations/base.rb
35
+ - lib/dynamic_active_resource/associations/belongs_to.rb
36
+ - lib/dynamic_active_resource/associations/has_many.rb
37
+ - lib/dynamic_active_resource/associations.rb
38
+ - lib/dynamic_active_resource/base.rb
39
+ - lib/dynamic_active_resource/common_class_methods.rb
40
+ - lib/dynamic_active_resource/common_dynamic_class_instance_methods.rb
41
+ - lib/dynamic_active_resource/helpers.rb
42
+ - lib/dynamic_active_resource/version.rb
43
+ - lib/dynamic_active_resource.rb
34
44
  - README
35
45
  - History.txt
36
46
  - init.rb